Import Cobalt 12.88774

Change-Id: Id685e20e1b1f8fec13607b39f552fad3cc76ebae
diff --git a/src/starboard/BUILD.gn b/src/starboard/BUILD.gn
new file mode 100644
index 0000000..8adaf0c
--- /dev/null
+++ b/src/starboard/BUILD.gn
@@ -0,0 +1,97 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+group("all") {
+  testonly = true
+  deps = [
+    # TODO: uncomment as each of these are implemented
+    # "//starboard/client_porting/eztime:all",
+    # "//starboard/client_porting/icu_init:all",
+    # "//starboard/client_porting/poem:all",
+    "//starboard/examples",
+    # "//starboard/nplb/blitter_pixel_tests:all",
+    "//starboard/nplb:all",
+    ":starboard",
+  ]
+
+  has_platform_tests =
+      exec_script("//build/dir_exists.py",
+                  [ rebase_path("//$starboard_path/tests", root_build_dir) ],
+                  "string") == "True"
+  if (has_platform_tests) {
+    deps += [ "//$starboard_path/tests" ]
+  }
+}
+
+source_set("starboard") {
+  sources = [
+    "atomic.h",
+    "audio_sink.h",
+    "blitter.h",
+    "byte_swap.h",
+    "character.h",
+    "condition_variable.h",
+    "configuration.h",
+    "decode_target.h",
+    "directory.h",
+    "double.h",
+    "drm.h",
+    "event.h",
+    "export.h",
+    "file.h",
+    "input.h",
+    "key.h",
+    "log.h",
+    "media.h",
+    "memory.h",
+    "microphone.h",
+    "mutex.h",
+    "once.h",
+    "player.h",
+    "queue.h",
+    "socket.h",
+    "socket_waiter.h",
+    "spin_lock.h",
+    "storage.h",
+    "string.h",
+    "system.h",
+    "thread.h",
+    "thread_types.h",
+    "time.h",
+    "time_zone.h",
+    "types.h",
+    "user.h",
+    "window.h",
+    "//starboard/shared/media_session/playback_state.h",
+  ]
+
+  private_sources = exec_script("tools/find_private_files.py",
+                                [
+                                  rebase_path("//", root_build_dir),
+                                  "*.h",
+                                ],
+                                "list lines",
+                                [ "//starboard/private" ])
+  sources += rebase_path(private_sources, ".", root_build_dir)
+
+  deps = [
+    "//starboard/common",
+  ]
+
+  public_deps = [
+    "//$starboard_path:starboard_platform",
+  ]
+
+  # TODO: handle shared_main_adapter.cc
+}
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index acbe7b4..6bcc0d7 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -8,12 +8,18 @@
 
 ## Version 6
 
-### Introduce pointer (mouse) input support.
+### 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.
+### Flexible audio specific config
 
 `SbMediaAudioHeader::audio_specific_config` will be a pointer instead of an
 array.
@@ -26,7 +32,7 @@
 Changes `SbTimeZoneGetName()` to be more flexible in what it is allowed to
 return.
 
-### SbDecodeTargetNumberOfPlanesForFormat
+### `SbDecodeTargetNumberOfPlanesForFormat`
 
 Adds the convenience inline function, SbDecodeTargetNumberOfPlanesForFormat() to
 `starboard/decode_target.h`.
@@ -40,7 +46,7 @@
 
 Removes `SbSystemPlatformErrorType` values specific to user status.
 
-### SbDecodeTarget support for the UYVY (i.e. YUV 422) format
+### `SbDecodeTarget` support for the UYVY (i.e. YUV 422) format
 
 Add support for UYVY decode targets (e.g. YUV 422) via the
 `kSbDecodeTargetFormat1PlaneUYVY` enum.
@@ -50,15 +56,19 @@
 This adds SbKey codes for the colored keys found on most contemporary TV
 remotes.
 
-### kSbEventTypeLowMemory
+### `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()
+### 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.
+
 ## Version 5
 
 ### Add Speech Recognizer API
diff --git a/src/starboard/README.md b/src/starboard/README.md
index 1487a1d..9f2d47f 100644
--- a/src/starboard/README.md
+++ b/src/starboard/README.md
@@ -5,6 +5,15 @@
 that it does not.
 
 
+## GN Migration Notice
+
+Cobalt and Starboard are currently migrating from the GYP build system to the GN
+build system. This readme contains instructions for both systems. As of now, GN
+is not ready for general use for Cobalt/Starboard. If you are not a core Cobalt
+developer, you should probably ignore the sections titled "GN Instructions" for
+now.
+
+
 ## Interesting Source Locations
 
 All source locations are specified relative to `src/starboard/` (this directory).
@@ -23,17 +32,6 @@
     platforms that share some facet of their OS API.
 
 
-## Building with Starboard
-
-Follow the Cobalt instructions, except when invoking gyp:
-
-    $ cobalt/build/gyp_cobalt -C debug linux-x64x11
-
-and when invoking ninja:
-
-    $ ninja -C out/linux-x64x11_debug cobalt
-
-
 ## Quick Guide to Starting a Port
 
 ### I. Enumerate and Name Your Platform Configurations
@@ -113,6 +111,31 @@
 
 And so on.
 
+#### GN Instructions
+
+Each `<binary-variant>/` directory must have at least `configuration_public.h`,
+`atomic_public.h`, `thread_types_public.h`, `BUILD.gn`, `configuration.gni`,
+and `buildconfig.gni`.
+
+In the BobCo's BobBox example, we would see a directory tree like:
+
+  * `src/third_party/starboard/bobbox/`
+      * `shared/`
+      * `mipseb/`
+          * `buildconfig.gni`
+          * `configuration.gni`
+          * `atomic_public.h`
+          * `BUILD.gn`
+          * `configuration_public.h`
+          * `thread_types_public.h`
+      * `mipsel/`
+          * `buildconfig.gni`
+          * `configuration.gni`
+          * `atomic_public.h`
+          * `BUILD.gn`
+          * `configuration_public.h`
+          * `thread_types_public.h`
+
 
 ### III. Base Your Port on a Reference Port
 
@@ -160,6 +183,26 @@
 `src/third_party/starboard/bobbox/mipseb/gyp_configuration.py`, it would choose
 the platform configuration name `bobbox-mipseb`.)
 
+#### GN Instructions
+
+Update `<binary-variant>/BUILD.gn` to point at all the source files that you
+want to build as your new Starboard implementation. The `//` expression in GN
+refers to the `src/` directory of your source tree. Otherwise, files are assumed
+to be relative to the directory the `BUILD.gn` or `.gni` file is in. The
+`BUILD.gn` file contains absolute paths, so the paths will still be valid if you
+copy it to a new directory. You can then incrementally replace files with new
+implementations as necessary.
+
+In order to use a new platform configuration in a build, you need to ensure that
+you have a `BUILD.gn`, `configuration.gni`, and `buildconfig.gni` in their
+own directory for each binary variant, plus the header files
+`configuration_public.h`, `atomic_public.h`, and `thread_types_public.h`. The GN
+build will scan your directories for these files, and then calculate a port name
+based on the directories between `src/third_party/starboard` and your
+`configuration.gni` files. (e.g. for
+`src/third_party/starboard/bobbox/mipseb/configuration.gni`, it would choose the
+platform configuration name `bobbox-mipseb`.)
+
 
 ### IV. A New Port, Step-by-Step
 
@@ -179,7 +222,7 @@
          toolchain analogs for the toolchain for your platform.
   1. In `gyp_configuration.gypi`
       1. Update the names of the configurations and the default_configuration to
-         be `<platform-configuation>_<build-type>` for your platform
+         be `<platform-configuration>_<build-type>` for your platform
          configuration name, where `<build-type>` is one of `debug`, `devel`,
          `qa`, `gold`.
       1. Update your platform variables.
@@ -213,6 +256,46 @@
 This will attempt to build the "No Platform Left Behind" test suite with your
 new Starboard implementation, and you are ready to start porting!
 
+#### GN Instructions
+
+Follow the above list, except:
+
+  1. Ignore the steps about `gyp_configuration.py` and `gyp_configuration.gypi`.
+  1. Update `BUILD.gn` instead of `starboard_platform.gyp`.
+  1. Also in `BUILD.gn`:
+      1. Update your toolchain command-line flags and libraries, for all
+         configurations as well as for each individual configuration.
+      1. Implement generic compiler configs such as `sb_pedantic_warnings`
+         and `rtti`.
+      1. If you're not using a predefined toolchain, define one.
+  1. In `buildconfig.gni`:
+      1. If your platform is Linux-based, set `target_os_ = "linux"`.
+      1. Set `target_cpu_` to your target architecture (e.g. `arm`, `ppc`,
+         `x64`, `x86`, `mips`).
+      1. Set the target and host toolchains. If you defined a target
+         toolchain in `BUILD.gn`, you'll want to set it to that.
+  1. In `configuration.gni`, set platform-specific defaults for any
+     variables that should be overriden for your Starboard platform.  You
+     can find a list of such variables at `//cobalt/build/config/base.gni`
+     and `//starboard/build/config/base.gni`.
+
+
+You should now be able to run GN with your new port. From your `src/` directory:
+
+    $ gn args out/bobbox-mipseb_debug
+
+An editor will open up. Type into the editor:
+
+    cobalt_config = "debug"
+    target_platform = "bobbox-mipseb"
+
+Save and close the editor. Then run
+
+    $ ninja -C out/bobbox-mipseb_debug nplb
+
+This will attempt to build the "No Platform Left Behind" test suite with your
+new Starboard implementation, and you are ready to start porting!
+
 
 ## Suggested Implementation Order
 
diff --git a/src/starboard/build/config/BUILD.gn b/src/starboard/build/config/BUILD.gn
new file mode 100644
index 0000000..73454f9
--- /dev/null
+++ b/src/starboard/build/config/BUILD.gn
@@ -0,0 +1,91 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: change to //starboard eventually
+import("//cobalt/build/config/base.gni")
+import("//starboard/build/delegated_config.gni")
+
+# =============================================================================
+# COMPILER DEFAULTS
+# =============================================================================
+
+# TODO: only set these defines on the files that actually need them
+config("compiler_defaults") {
+  # The canonical way to detect Starboard is #if defined(STARBOARD). This
+  # should be defined both when building Starboard itself, and when building
+  # any other source file in a Starboard-based project.
+  defines = [ "STARBOARD" ]
+
+  if (gl_type == "none") {
+    defines += [ "SB_GYP_GL_TYPE_IS_NONE=1" ]
+  } else {
+    defines += [ "SB_GYP_GL_TYPE_IS_NONE=0" ]
+  }
+
+  if (abort_on_allocation_failure) {
+    defines += [ "SB_ABORT_ON_ALLOCATION_FAILURE" ]
+  }
+
+  if (sb_allow_memory_tracking) {
+    defines += [ "STARBOARD_ALLOWS_MEMORY_TRACKING" ]
+  }
+
+  if (sb_enable_lib) {
+    defines += [ "SB_IS_LIBRARY=1" ]
+  }
+
+  # There doesn't appear to be any way to use the C preprocessor to do
+  # string concatenation with the / character. This prevents us from using
+  # the preprocessor to assemble an include file path, so we have to do
+  # the concatenation here in GN.
+  # http://stackoverflow.com/questions/29601786/c-preprocessor-building-a-path-string
+  defines += [
+    "STARBOARD_ATOMIC_INCLUDE=\"$starboard_path/atomic_public.h\"",
+    "STARBOARD_CONFIGURATION_INCLUDE=\"$starboard_path/configuration_public.h\"",
+    "STARBOARD_THREAD_TYPES_INCLUDE=\"$starboard_path/thread_types_public.h\"",
+  ]
+
+  # TODO: find a way to remove this dependence on Cobalt
+  if (sb_media_platform == "starboard") {
+    defines += [ "SB_GYP_CAN_MEDIA_USE_STARBOARD_PIPELINE=1" ]
+  } else {
+    defines += [ "SB_GYP_CAN_MEDIA_USE_STARBOARD_PIPELINE=0" ]
+  }
+}
+
+# =============================================================================
+# DELEGATED CONFIGS
+# =============================================================================
+
+# Enables pedantic levels of warnings for the current toolchain.
+delegated_config("pedantic_warnings") {
+  path = "//$starboard_path"
+  generate_default = false
+}
+
+# Controls the optimization level
+delegated_config("optimizations") {
+  path = "//$starboard_path"
+  prefixes = [ "no", "debuggable", "full" ]
+}
+
+# Enables/disables rtti
+delegated_config("rtti") {
+  path = "//$starboard_path"
+}
+
+# Enables the exit-time-destructors warning.
+config("wexit_time_destructors") {
+  configs = [ "//$starboard_path:wexit_time_destructors" ]
+}
diff --git a/src/starboard/build/config/base.gni b/src/starboard/build/config/base.gni
new file mode 100644
index 0000000..7599e71
--- /dev/null
+++ b/src/starboard/build/config/base.gni
@@ -0,0 +1,81 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Import platform-specific defaults
+import("//$starboard_path/configuration.gni")
+
+# Starboard variables.
+
+# Enables embedding Starboard as a shared library within another app. This
+# requires a 'lib' starboard implementation for the corresponding platform.
+if (!defined(sb_enable_lib)) {
+  sb_enable_lib = false
+}
+
+# Directory path to static contents.
+if (!defined(sb_static_contents_output_base_dir)) {
+  sb_static_contents_output_base_dir = "$root_out_dir/content"
+}
+
+# Directory path to static contents' data.
+if (!defined(sb_static_contents_output_data_dir)) {
+  sb_static_contents_output_data_dir = "$root_out_dir/content/data"
+}
+
+# Halt execution on failure to allocate memory.
+if (!defined(abort_on_allocation_failure)) {
+  abort_on_allocation_failure = true
+}
+
+# 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
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "system_gles2" - Use the system implementation of EGL + GLES2. The
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "glimp"  - Cobalt's own EGL + GLES2 implementation. This requires a
+#              valid Glimp implementation for the platform.
+#   "angle"  - A DirectX-to-OpenGL adaptation layer. This requires a valid
+#              ANGLE implementation for the platform.
+# Choosing an unsupported value will result in a GN error:
+#   "Unresolved dependencies: //cobalt/renderer/egl_and_gles:<gl_type>"
+if (!defined(gl_type)) {
+  gl_type = "system_gles2"
+}
+
+# TODO: Temporary indicator for Tizen - should eventually move to feature defines.
+if (!defined(is_tizen_os)) {
+  is_tizen_os = false
+}
+
+# The event polling mechanism available on this platform to support libevent.
+# Platforms may redefine to "poll" if necessary.
+# Other mechanisms, e.g. devpoll, kqueue, select, are not yet supported.
+if (!defined(sb_libevent_method)) {
+  sb_libevent_method = "epoll"
+}
+
+# Whether to allow memory tracking
+if (!defined(sb_allow_memory_tracking)) {
+  sb_allow_memory_tracking = (cobalt_config != "gold" && !sb_enable_lib)
+}
+
+
+# The system root for cross-compiles. Default: none.
+if (!defined(sysroot)) {
+  sysroot = ""
+}
diff --git a/src/starboard/build/config/fastbuild.gni b/src/starboard/build/config/fastbuild.gni
new file mode 100644
index 0000000..b1fe040
--- /dev/null
+++ b/src/starboard/build/config/fastbuild.gni
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # If set to true, omits the generation of debugging symbols
+  cobalt_use_fastbuild = false
+}
diff --git a/src/starboard/build/config/sanitizers.gni b/src/starboard/build/config/sanitizers.gni
new file mode 100644
index 0000000..a1e9817
--- /dev/null
+++ b/src/starboard/build/config/sanitizers.gni
@@ -0,0 +1,40 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Allow for platform-specific customization of use_asan_by_default
+import("//$starboard_path/configuration.gni")
+
+declare_args() {
+  # Compile for Thread Sanitizer to find threading bugs.
+  use_tsan = false
+}
+
+declare_args() {
+  # Compile for Address Sanitizer to find memory bugs.
+  use_asan =
+      !use_tsan && (cobalt_config == "debug" || cobalt_config == "devel") &&
+      (defined(use_asan_by_default) && use_asan_by_default)
+}
+
+assert(!(use_asan && use_tsan), "ASan and TSan are mutually exclusive")
+
+if (current_toolchain != default_toolchain) {
+  # Disable sanitizers for non-default toolchains.
+  use_asan = false
+  use_tsan = false
+}
diff --git a/src/starboard/build/delegated_config.gni b/src/starboard/build/delegated_config.gni
new file mode 100644
index 0000000..cc96a51
--- /dev/null
+++ b/src/starboard/build/delegated_config.gni
@@ -0,0 +1,111 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The delegated_config template defines a family of configs which delegate to
+# real implementations elsewhere. The most notable use of this template is to
+# define toolchain-independent compiler configs, such as
+# //starboard/build/config:rtti (and its sisters :no_rtti and :default_rtti).
+#
+# The simplest way to use delegated_config is to specify only a path:
+#
+#   delegated_config("rtti") {
+#     path = "//$starboard_path"
+#   }
+#
+# The sample code above will expand to:
+#
+#   config("rtti") {
+#     configs = [ "//$starboard_path:rtti" ]
+#   }
+#
+#   config("no_rtti") {
+#     configs = [ "//$starboard_path:no_rtti" ]
+#   }
+#
+#   config("default_rtti") {
+#     configs = [ "//$starboard_path:default_rtti" ]
+#   }
+#
+# The BUILD.gn file is then expected to define each of those three configs (or
+# further delegate them to e.g. a toolchain's implementation).
+#
+# To use a different list of prefixes, set the prefixes option:
+#
+#   delegated_config("optimizations") {
+#     path = "//$starboard_path"
+#     prefixes = [ "no", "debuggable", "full" ]
+#   }
+#
+# This will expand to:
+#
+#   config("no_optimizations") {
+#     configs = [ "//$starboard_path:no_optimizations" ]
+#   }
+#
+#   config("debuggable_optimizations") {
+#     configs = [ "//$starboard_path:debuggable_optimizations" ]
+#   }
+#
+#   config("full_optimizations") {
+#     configs = [ "//$starboard_path:full_optimizations" ]
+#   }
+#
+#   config("default_optimizations") {
+#     configs = [ "//$starboard_path:default_optimizations" ]
+#   }
+#
+# Note that the default_optimizations config is generated, even though it is
+# not in the list of prefixes. If you don't want the default_optimizations
+# config to be generated, set generate_default = false:
+#
+#   delegated_config("optimizations") {
+#     path = "//$starboard_path"
+#     prefixes = [ "no", "debuggable", "full" ]
+#     generate_default = false
+#   }
+#
+# Now only the no_, debuggable_, and full_ configs will be generated.
+
+template("delegated_config") {
+  assert(defined(invoker.path),
+         "You need to specify a path to the implementation")
+
+  if (defined(invoker.prefixes)) {
+    prefixes = invoker.prefixes
+  } else {
+    prefixes = [ "", "no" ]
+  }
+
+  foreach(prefix, prefixes) {
+    assert(prefix != "default",
+           "You shouldn't include \"default\" in the list of prefixes " +
+               "because it's separately generated.")
+
+    if (prefix == "") {
+      prefix_ = prefix
+    } else {
+      prefix_ = "${prefix}_"
+    }
+
+    config("$prefix_$target_name") {
+      configs = [ "${invoker.path}:$prefix_$target_name" ]
+    }
+  }
+
+  if (!(defined(invoker.generate_default) && !invoker.generate_default)) {
+    config("default_$target_name") {
+      configs = [ "${invoker.path}:default_$target_name" ]
+    }
+  }
+}
diff --git a/src/starboard/build/deploy.gni b/src/starboard/build/deploy.gni
new file mode 100644
index 0000000..0cd9369
--- /dev/null
+++ b/src/starboard/build/deploy.gni
@@ -0,0 +1,52 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This header file is meant to be included to provide a rule to deploy
+# a target on a target platform.
+#
+# Platforms needing such a deploy rule should define a template called
+# "deploy", which defines an action target which performs the necessary
+# actions (such as copying per-executable metadata files to the output
+# directory). This template should preferably be defined in
+# //$starboard_path/deploy.gni.
+#
+# To use this, at the top of the BUILD.gn file, import
+# //starboard/build/deploy.gni. Then, define a deploy target:
+#
+# deploy("deploy") {
+#   executable_name = "the_primary_targets_name"
+# }
+#
+# See //starboard/nplb/BUILD.gn for an example.
+
+import("//$starboard_path/configuration.gni")
+
+if (defined(include_path_platform_deploy_gni)) {
+  import(include_path_platform_deploy_gni)
+} else {
+  # The default deploy action is to do nothing.
+  template("deploy") {
+    not_needed(invoker, "*")
+
+    # Create an empty target so we can still depend on it. Honor the testonly
+    # and visibility settings.
+    group(target_name) {
+      forward_variables_from(invoker,
+                             [
+                               "testonly",
+                               "visibility",
+                             ])
+    }
+  }
+}
diff --git a/src/starboard/build/toolchain/BUILD.gn b/src/starboard/build/toolchain/BUILD.gn
new file mode 100644
index 0000000..daf2fb3
--- /dev/null
+++ b/src/starboard/build/toolchain/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//starboard/build/toolchain/concurrent_links.gni")
+
+if (current_toolchain == default_toolchain) {
+  pool("link_pool") {
+    depth = concurrent_links
+  }
+}
diff --git a/src/starboard/build/toolchain/asan_symbolizer_path.gni b/src/starboard/build/toolchain/asan_symbolizer_path.gni
new file mode 100644
index 0000000..8ca146d
--- /dev/null
+++ b/src/starboard/build/toolchain/asan_symbolizer_path.gni
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/sanitizers.gni")
+import("//starboard/build/toolchain/clang.gni")
+
+# TODO: is it really necessary to check whether ASAN is on here?
+if (use_asan) {
+  asan_symbolizer_path = "$clang_base_path/bin/llvm-symbolizer"
+} else {
+  asan_symbolizer_path = ""
+}
diff --git a/src/starboard/build/toolchain/clang.gni b/src/starboard/build/toolchain/clang.gni
new file mode 100644
index 0000000..6ef471a
--- /dev/null
+++ b/src/starboard/build/toolchain/clang.gni
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  clang_base_path = ""
+}
+
+if (clang_base_path == "") {
+  # clang_base_path depends on gyp_utils.py for its _CLANG_VERSION variable
+  clang_base_path = exec_script("get_clang_base_path.py", [], "trim string",
+                                [ "//cobalt/build/gyp_utils.py" ])
+}
diff --git a/src/starboard/build/toolchain/concurrent_links.gni b/src/starboard/build/toolchain/concurrent_links.gni
new file mode 100644
index 0000000..22ac7e9
--- /dev/null
+++ b/src/starboard/build/toolchain/concurrent_links.gni
@@ -0,0 +1,45 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file should only be imported from files that define toolchains.
+# There's no way to enforce this exactly, but all toolchains are processed
+# in the context of the default_toolchain, so we can at least check for that.
+assert(current_toolchain == default_toolchain)
+
+declare_args() {
+  # Limit the number of concurrent links; we often want to run fewer
+  # links at once than we do compiles, because linking is memory-intensive.
+  # The default to use varies by platform and by the amount of memory
+  # available, so we call out to a script to get the right value.
+  concurrent_links = -1
+}
+
+if (concurrent_links == -1) {
+  if (host_os == "win") {
+    _args = [ "--mem_per_link_gb=5" ]
+  } else if (host_os == "mac") {
+    _args = [ "--mem_per_link_gb=4" ]
+  }
+  else {
+    _args = []
+  }
+
+  # TODO(crbug.com/617429) Pass more build configuration info to the script
+  # so that we can compute better values.
+  concurrent_links = exec_script("get_concurrent_links.py", _args, "value")
+}
diff --git a/src/starboard/build/toolchain/gcc_ar_wrapper.py b/src/starboard/build/toolchain/gcc_ar_wrapper.py
new file mode 100755
index 0000000..4663f98
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_ar_wrapper.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Runs the 'ar' command after removing its output file first.
+
+This script is invoked like:
+  python gcc_ar_wrapper.py --ar=$AR --output=$OUT $OP $INPUTS
+to do the equivalent of:
+  rm -f $OUT && $AR $OP $OUT $INPUTS
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+import wrapper_utils
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--ar',
+                      required=True,
+                      help='The ar binary to run',
+                      metavar='PATH')
+  parser.add_argument('--output',
+                      required=True,
+                      help='Output archive file',
+                      metavar='ARCHIVE')
+  parser.add_argument('--plugin',
+                      help='Load plugin')
+  parser.add_argument('operation',
+                      help='Operation on the archive')
+  parser.add_argument('inputs', nargs='+',
+                      help='Input files')
+  args = parser.parse_args()
+
+  command = [args.ar, args.operation]
+  if args.plugin is not None:
+    command += ['--plugin', args.plugin]
+  command.append(args.output)
+  command += args.inputs
+
+  # Remove the output file first.
+  try:
+    os.remove(args.output)
+  except OSError as e:
+    if e.errno != os.errno.ENOENT:
+      raise
+
+  # Now just run the ar command.
+  return subprocess.call(wrapper_utils.CommandToRun(command))
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/gcc_link_wrapper.py b/src/starboard/build/toolchain/gcc_link_wrapper.py
new file mode 100755
index 0000000..b964347
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_link_wrapper.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Runs a linking command and optionally a strip command.
+
+This script exists to avoid using complex shell commands in
+gcc_toolchain.gni's tool("link"), in case the host running the compiler
+does not have a POSIX-like shell (e.g. Windows).
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+import wrapper_utils
+
+
+# When running on a Windows host and using a toolchain whose tools are
+# actually wrapper scripts (i.e. .bat files on Windows) rather than binary
+# executables, the "command" to run has to be prefixed with this magic.
+# The GN toolchain definitions take care of that for when GN/Ninja is
+# running the tool directly.  When that command is passed in to this
+# script, it appears as a unitary string but needs to be split up so that
+# just 'cmd' is the actual command given to Python's subprocess module.
+BAT_PREFIX = 'cmd /c call '
+
+
+def CommandToRun(command):
+  if command[0].startswith(BAT_PREFIX):
+    command = command[0].split(None, 3) + command[1:]
+  return command
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--strip',
+                      help='The strip binary to run',
+                      metavar='PATH')
+  parser.add_argument('--unstripped-file',
+                      help='Executable file produced by linking command',
+                      metavar='FILE')
+  parser.add_argument('--map-file',
+                      help=('Use --Wl,-Map to generate a map file. Will be '
+                            'gzipped if extension ends with .gz'),
+                      metavar='FILE')
+  parser.add_argument('--output',
+                      required=True,
+                      help='Final output executable file',
+                      metavar='FILE')
+  parser.add_argument('command', nargs='+',
+                      help='Linking command')
+  args = parser.parse_args()
+
+  # Work-around for gold being slow-by-default. http://crbug.com/632230
+  fast_env = dict(os.environ)
+  fast_env['LC_ALL'] = 'C'
+  result = wrapper_utils.RunLinkWithOptionalMapFile(args.command, env=fast_env,
+                                                    map_file=args.map_file)
+  if result != 0:
+    return result
+
+  # Finally, strip the linked executable (if desired).
+  if args.strip:
+    result = subprocess.call(CommandToRun([
+        args.strip, '--strip-unneeded', '-o', args.output, args.unstripped_file
+        ]))
+
+  return result
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/gcc_solink_wrapper.py b/src/starboard/build/toolchain/gcc_solink_wrapper.py
new file mode 100755
index 0000000..71af5e3
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_solink_wrapper.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged.
+
+This script exists to avoid using complex shell commands in
+gcc_toolchain.gni's tool("solink"), in case the host running the compiler
+does not have a POSIX-like shell (e.g. Windows).
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+import wrapper_utils
+
+
+def CollectSONAME(args):
+  """Replaces: readelf -d $sofile | grep SONAME.
+
+  Args:
+    args: the args to pass to the subprocess
+
+  Returns:
+    A tuple of (return code, output)
+  """
+  toc = ''
+  readelf = subprocess.Popen(wrapper_utils.CommandToRun(
+      [args.readelf, '-d', args.sofile]), stdout=subprocess.PIPE, bufsize=-1)
+  for line in readelf.stdout:
+    if 'SONAME' in line:
+      toc += line
+  return readelf.wait(), toc
+
+
+def CollectDynSym(args):
+  """Replaces: nm --format=posix -g -D $sofile | cut -f1-2 -d' '.
+
+  Args:
+    args: the args to pass to the subprocess
+
+  Returns:
+    A tuple of (return code, output)
+  """
+  toc = ''
+  nm = subprocess.Popen(wrapper_utils.CommandToRun([
+      args.nm, '--format=posix', '-g', '-D', args.sofile]),
+                        stdout=subprocess.PIPE, bufsize=-1)
+  for line in nm.stdout:
+    toc += ' '.join(line.split(' ', 2)[:2]) + '\n'
+  return nm.wait(), toc
+
+
+def CollectTOC(args):
+  result, toc = CollectSONAME(args)
+  if result == 0:
+    result, dynsym = CollectDynSym(args)
+    toc += dynsym
+  return result, toc
+
+
+def UpdateTOC(tocfile, toc):
+  if os.path.exists(tocfile):
+    old_toc = open(tocfile, 'r').read()
+  else:
+    old_toc = None
+  if toc != old_toc:
+    open(tocfile, 'w').write(toc)
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--readelf',
+                      required=True,
+                      help='The readelf binary to run',
+                      metavar='PATH')
+  parser.add_argument('--nm',
+                      required=True,
+                      help='The nm binary to run',
+                      metavar='PATH')
+  parser.add_argument('--strip',
+                      help='The strip binary to run',
+                      metavar='PATH')
+  parser.add_argument('--sofile',
+                      required=True,
+                      help='Shared object file produced by linking command',
+                      metavar='FILE')
+  parser.add_argument('--tocfile',
+                      required=True,
+                      help='Output table-of-contents file',
+                      metavar='FILE')
+  parser.add_argument('--map-file',
+                      help=('Use --Wl,-Map to generate a map file. Will be '
+                            'gzipped if extension ends with .gz'),
+                      metavar='FILE')
+  parser.add_argument('--output',
+                      required=True,
+                      help='Final output shared object file',
+                      metavar='FILE')
+  parser.add_argument('--resource-whitelist',
+                      help='Merge all resource whitelists into a single file.',
+                      metavar='PATH')
+  parser.add_argument('command', nargs='+',
+                      help='Linking command')
+  args = parser.parse_args()
+
+  # Work-around for gold being slow-by-default. http://crbug.com/632230
+  fast_env = dict(os.environ)
+  fast_env['LC_ALL'] = 'C'
+
+  if args.resource_whitelist:
+    whitelist_candidates = wrapper_utils.ResolveRspLinks(args.command)
+    wrapper_utils.CombineResourceWhitelists(
+        whitelist_candidates, args.resource_whitelist)
+
+  # First, run the actual link.
+  command = wrapper_utils.CommandToRun(args.command)
+  result = wrapper_utils.RunLinkWithOptionalMapFile(command, env=fast_env,
+                                                    map_file=args.map_file)
+
+  if result != 0:
+    return result
+
+  # Next, generate the contents of the TOC file.
+  result, toc = CollectTOC(args)
+  if result != 0:
+    return result
+
+  # If there is an existing TOC file with identical contents, leave it alone.
+  # Otherwise, write out the TOC file.
+  UpdateTOC(args.tocfile, toc)
+
+  # Finally, strip the linked shared object file (if desired).
+  if args.strip:
+    result = subprocess.call(wrapper_utils.CommandToRun(
+        [args.strip, '--strip-unneeded', '-o', args.output, args.sofile]))
+
+  return result
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/gcc_toolchain.gni b/src/starboard/build/toolchain/gcc_toolchain.gni
new file mode 100644
index 0000000..94c52f4
--- /dev/null
+++ b/src/starboard/build/toolchain/gcc_toolchain.gni
@@ -0,0 +1,487 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/toolchain/clang.gni")
+import("//starboard/build/toolchain/goma.gni")
+
+# This template defines a toolchain for something that works like gcc
+# (including clang).
+#
+# It requires the following variables specifying the executables to run:
+#  - ar
+#  - cc
+#  - cxx
+#  - ld
+#
+# Optional parameters that control the tools:
+#
+#  - extra_cflags
+#      Extra flags to be appended when compiling C files (but not C++ files).
+#  - extra_cppflags
+#      Extra flags to be appended when compiling both C and C++ files. "CPP"
+#      stands for "C PreProcessor" in this context, although it can be
+#      used for non-preprocessor flags as well. Not to be confused with
+#      "CXX" (which follows).
+#  - extra_cxxflags
+#      Extra flags to be appended when compiling C++ files (but not C files).
+#  - extra_ldflags
+#      Extra flags to be appended when linking
+#
+#  - libs_section_prefix
+#  - libs_section_postfix
+#      The contents of these strings, if specified, will be placed around
+#      the libs section of the linker line. It allows one to inject libraries
+#      at the beginning and end for all targets in a toolchain.
+#  - solink_libs_section_prefix
+#  - solink_libs_section_postfix
+#      Same as libs_section_{pre,post}fix except used for solink instead of link.
+#  - link_outputs
+#      The content of this array, if specified, will be added to the list of
+#      outputs from the link command. This can be useful in conjunction with
+#      the post_link parameter.
+#  - post_link
+#      The content of this string, if specified, will be run as a separate
+#      command following the the link command.
+#  - deps
+#      Just forwarded to the toolchain definition.
+#  - executable_extension
+#      If this string is specified it will be used for the file extension
+#      for an executable, rather than using no extension; targets will
+#      still be able to override the extension using the output_extension
+#      variable.
+#  - rebuild_define
+#      The contents of this string, if specified, will be passed as a #define
+#      to the toolchain. It can be used to force recompiles whenever a
+#      toolchain is updated.
+#  - shlib_extension
+#      If this string is specified it will be used for the file extension
+#      for a shared library, rather than default value specified in
+#      toolchain.gni
+#  - strip
+#      Location of the strip executable. When specified, strip will be run on
+#      all shared libraries and executables as they are built. The pre-stripped
+#      artifacts will be put in lib.unstripped/ and exe.unstripped/.
+template("gcc_toolchain") {
+  toolchain(target_name) {
+    assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
+    assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value")
+    assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value")
+    assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value")
+    assert(defined(invoker.shlib_extension),
+           "gcc_toolchain() must specify a \"shlib_extension\" value")
+
+    # This define changes when the toolchain changes, forcing a rebuild.
+    # Nothing should ever use this define.
+    if (defined(invoker.rebuild_define)) {
+      rebuild_string = "-D" + invoker.rebuild_define + " "
+    } else {
+      rebuild_string = ""
+    }
+
+    # GN's syntax can't handle more than one scope dereference at once, like
+    # "invoker.toolchain_args.foo", so make a temporary to hold the toolchain
+    # args so we can do "invoker_toolchain_args.foo".
+    assert(defined(invoker.toolchain_args),
+           "Toolchains must specify toolchain_args")
+    invoker_toolchain_args = invoker.toolchain_args
+    assert(defined(invoker_toolchain_args.current_cpu),
+           "toolchain_args must specify a current_cpu")
+    assert(defined(invoker_toolchain_args.current_os),
+           "toolchain_args must specify a current_os")
+
+    # When invoking this toolchain not as the default one, these args will be
+    # passed to the build. They are ignored when this is the default toolchain.
+    toolchain_args = {
+      # Populate toolchain args from the invoker.
+      forward_variables_from(invoker_toolchain_args, "*")
+    }
+
+    # When the invoker has explicitly overridden use_goma or cc_wrapper in the
+    # toolchain args, use those values, otherwise default to the global one.
+    # This works because the only reasonable override that toolchains might
+    # supply for these values are to force-disable them.
+    if (defined(toolchain_args.use_goma)) {
+      toolchain_uses_goma = toolchain_args.use_goma
+    } else {
+      toolchain_uses_goma = use_goma
+    }
+
+    # When the invoker has explicitly overridden use_goma in the
+    # toolchain args, use those values, otherwise default to the global one.
+    # This works because the only reasonable override that toolchains might
+    # supply for these values are to force-disable them.
+    if (toolchain_uses_goma) {
+      goma_path = "$goma_dir/gomacc"
+      compiler_prefix = "${goma_path} "
+    } else {
+      compiler_prefix = ""
+    }
+
+    cc = compiler_prefix + invoker.cc
+    cxx = compiler_prefix + invoker.cxx
+    ar = invoker.ar
+    ld = invoker.ld
+    if (!defined(asm)) {
+      asm = cc
+    }
+    if (defined(invoker.readelf)) {
+      readelf = invoker.readelf
+    } else {
+      readelf = "readelf"
+    }
+    if (defined(invoker.nm)) {
+      nm = invoker.nm
+    } else {
+      nm = "nm"
+    }
+    if (defined(invoker.stamp_command)) {
+      stamp_command = invoker.stamp_command
+    } else {
+      stamp_command = "touch {{output}}"
+    }
+    if (defined(invoker.copy_command)) {
+      copy_command = invoker.copy_command
+    } else {
+      copy_command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})"
+    }
+
+    default_shlib_extension = invoker.shlib_extension
+
+    if (defined(invoker.executable_extension)) {
+      default_executable_extension = invoker.executable_extension
+    } else {
+      default_executable_extension = ""
+    }
+
+    # Bring these into our scope for string interpolation with default values.
+    if (defined(invoker.libs_section_prefix)) {
+      libs_section_prefix = invoker.libs_section_prefix
+    } else {
+      libs_section_prefix = ""
+    }
+
+    if (defined(invoker.libs_section_postfix)) {
+      libs_section_postfix = invoker.libs_section_postfix
+    } else {
+      libs_section_postfix = ""
+    }
+
+    if (defined(invoker.solink_libs_section_prefix)) {
+      solink_libs_section_prefix = invoker.solink_libs_section_prefix
+    } else {
+      solink_libs_section_prefix = ""
+    }
+
+    if (defined(invoker.solink_libs_section_postfix)) {
+      solink_libs_section_postfix = invoker.solink_libs_section_postfix
+    } else {
+      solink_libs_section_postfix = ""
+    }
+
+    if (defined(invoker.extra_cflags) && invoker.extra_cflags != "") {
+      extra_cflags = " " + invoker.extra_cflags
+    } else {
+      extra_cflags = ""
+    }
+
+    if (defined(invoker.extra_cppflags) && invoker.extra_cppflags != "") {
+      extra_cppflags = " " + invoker.extra_cppflags
+    } else {
+      extra_cppflags = ""
+    }
+
+    if (defined(invoker.extra_cxxflags) && invoker.extra_cxxflags != "") {
+      extra_cxxflags = " " + invoker.extra_cxxflags
+    } else {
+      extra_cxxflags = ""
+    }
+
+    if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") {
+      extra_ldflags = " " + invoker.extra_ldflags
+    } else {
+      extra_ldflags = ""
+    }
+
+    # These library switches can apply to all tools below.
+    lib_switch = "-l"
+    lib_dir_switch = "-L"
+
+    # Object files go in this directory.
+    object_subdir = "{{target_out_dir}}/{{label_name}}"
+
+    tool("cc") {
+      depfile = "{{output}}.d"
+      command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "CC {{output}}"
+      outputs = [
+        "$object_subdir/{{source_name_part}}.o",
+      ]
+    }
+
+    tool("cxx") {
+      depfile = "{{output}}.d"
+      command = "$cxx -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}${extra_cppflags}${extra_cxxflags} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "CXX {{output}}"
+      outputs = [
+        "$object_subdir/{{source_name_part}}.o",
+      ]
+    }
+
+    tool("asm") {
+      # For GCC we can just use the C compiler to compile assembly.
+      depfile = "{{output}}.d"
+      command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "ASM {{output}}"
+      outputs = [
+        "$object_subdir/{{source_name_part}}.o",
+      ]
+    }
+
+    tool("alink") {
+      rspfile = "{{output}}.rsp"
+
+      # This needs a Python script to avoid using simple sh features in this
+      # command, in case the host does not use a POSIX shell (e.g. compiling
+      # POSIX-like toolchains such as NaCl on Windows).
+      ar_wrapper = rebase_path("//starboard/build/toolchain/gcc_ar_wrapper.py",
+                               root_build_dir)
+      extra_arflags = "rcsD"
+      command = "$python_path \"$ar_wrapper\" --output={{output}} --ar=\"$ar\" {{arflags}} $extra_arflags @\"$rspfile\""
+      description = "AR {{output}}"
+      rspfile_content = "{{inputs}}"
+      outputs = [
+        "{{output_dir}}/{{target_output_name}}{{output_extension}}",
+      ]
+
+      # Shared libraries go in the target out directory by default so we can
+      # generate different targets with the same name and not have them collide.
+      default_output_dir = "{{target_out_dir}}"
+      default_output_extension = ".a"
+      output_prefix = "lib"
+    }
+
+    tool("solink") {
+      soname = "{{target_output_name}}{{output_extension}}"  # e.g. "libfoo.so".
+      sofile = "{{output_dir}}/$soname"  # Possibly including toolchain dir.
+      rspfile = sofile + ".rsp"
+      pool = "//starboard/build/toolchain:link_pool($default_toolchain)"
+
+      if (defined(invoker.strip)) {
+        unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname"
+      } else {
+        unstripped_sofile = sofile
+      }
+
+      # These variables are not built into GN but are helpers that
+      # implement (1) linking to produce a .so, (2) extracting the symbols
+      # from that file (3) if the extracted list differs from the existing
+      # .TOC file, overwrite it, otherwise, don't change it.
+      tocfile = sofile + ".TOC"
+
+      link_command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
+
+      assert(defined(readelf), "to solink you must have a readelf")
+      assert(defined(nm), "to solink you must have an nm")
+      strip_switch = ""
+      if (defined(invoker.strip)) {
+        strip_switch = "--strip=${invoker.strip} "
+      }
+
+      # This needs a Python script to avoid using a complex shell command
+      # requiring sh control structures, pipelines, and POSIX utilities.
+      # The host might not have a POSIX shell and utilities (e.g. Windows).
+      solink_wrapper =
+          rebase_path("//starboard/build/toolchain/gcc_solink_wrapper.py",
+                      root_build_dir)
+      command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch--sofile=\"$unstripped_sofile\" --tocfile=\"$tocfile\" --output=\"$sofile\" -- $link_command"
+
+      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
+
+      description = "SOLINK $sofile"
+
+      # Use this for {{output_extension}} expansions unless a target manually
+      # overrides it (in which case {{output_extension}} will be what the target
+      # specifies).
+      default_output_extension = default_shlib_extension
+
+      default_output_dir = "{{root_out_dir}}"
+
+      output_prefix = "lib"
+
+      # Since the above commands only updates the .TOC file when it changes, ask
+      # Ninja to check if the timestamp actually changed to know if downstream
+      # dependencies should be recompiled.
+      restat = true
+
+      # Tell GN about the output files. It will link to the sofile but use the
+      # tocfile for dependency management.
+      outputs = [
+        sofile,
+        tocfile,
+      ]
+      if (sofile != unstripped_sofile) {
+        outputs += [ unstripped_sofile ]
+      }
+      link_output = sofile
+      depend_output = tocfile
+    }
+
+    tool("solink_module") {
+      soname = "{{target_output_name}}{{output_extension}}"  # e.g. "libfoo.so".
+      sofile = "{{output_dir}}/$soname"
+      rspfile = sofile + ".rsp"
+      pool = "//starboard/build/toolchain:link_pool($default_toolchain)"
+
+      if (defined(invoker.strip)) {
+        unstripped_sofile = "{{root_out_dir}}/lib.unstripped/$soname"
+      } else {
+        unstripped_sofile = sofile
+      }
+
+      command = "$ld -shared {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" -Wl,-soname=\"$soname\" @\"$rspfile\""
+
+      if (defined(invoker.strip)) {
+        strip_command = "${invoker.strip} --strip-unneeded -o \"$sofile\" \"$unstripped_sofile\""
+        command += " && " + strip_command
+      }
+      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix"
+
+      description = "SOLINK_MODULE $sofile"
+
+      # Use this for {{output_extension}} expansions unless a target manually
+      # overrides it (in which case {{output_extension}} will be what the target
+      # specifies).
+      if (defined(invoker.loadable_module_extension)) {
+        default_output_extension = invoker.loadable_module_extension
+      } else {
+        default_output_extension = default_shlib_extension
+      }
+
+      default_output_dir = "{{root_out_dir}}"
+
+      output_prefix = "lib"
+
+      outputs = [
+        sofile,
+      ]
+      if (sofile != unstripped_sofile) {
+        outputs += [ unstripped_sofile ]
+      }
+    }
+
+    tool("link") {
+      exename = "{{target_output_name}}{{output_extension}}"
+      outfile = "{{output_dir}}/$exename"
+      rspfile = "$outfile.rsp"
+      unstripped_outfile = outfile
+      pool = "//starboard/build/toolchain:link_pool($default_toolchain)"
+
+      # Use this for {{output_extension}} expansions unless a target manually
+      # overrides it (in which case {{output_extension}} will be what the target
+      # specifies).
+      default_output_extension = default_executable_extension
+
+      default_output_dir = "{{root_out_dir}}"
+
+      if (defined(invoker.strip)) {
+        unstripped_outfile = "{{root_out_dir}}/exe.unstripped/$exename"
+      }
+
+      start_group_flag = "-Wl,--start-group"
+      end_group_flag = "-Wl,--end-group "
+      link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag $libs_section_prefix {{libs}} $libs_section_postfix"
+
+      strip_switch = ""
+
+      if (defined(invoker.strip)) {
+        strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\""
+      }
+
+      link_wrapper =
+          rebase_path("//starboard/build/toolchain/gcc_link_wrapper.py",
+                      root_build_dir)
+      command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch -- $link_command"
+      description = "LINK $outfile"
+      rspfile_content = "{{inputs}}"
+      outputs = [
+        outfile,
+      ]
+      if (outfile != unstripped_outfile) {
+        outputs += [ unstripped_outfile ]
+      }
+      if (defined(invoker.link_outputs)) {
+        outputs += invoker.link_outputs
+      }
+    }
+
+    # These two are really entirely generic, but have to be repeated in
+    # each toolchain because GN doesn't allow a template to be used here.
+    # See //build/toolchain/toolchain.gni for details.
+    tool("stamp") {
+      command = stamp_command
+      description = "STAMP {{output}}"
+    }
+    tool("copy") {
+      command = copy_command
+      description = "COPY {{source}} {{output}}"
+    }
+
+    forward_variables_from(invoker, [ "deps" ])
+  }
+}
+
+# This is a shorthand for gcc_toolchain instances based on the Chromium-built
+# version of Clang. Only the toolchain_cpu and toolchain_os variables need to
+# be specified by the invoker, and optionally toolprefix if it's a
+# cross-compile case. Note that for a cross-compile case this toolchain
+# requires a config to pass the appropriate -target option, or else it will
+# actually just be doing a native compile. The invoker can optionally override
+# use_gold too.
+template("clang_toolchain") {
+  if (defined(invoker.toolprefix)) {
+    toolprefix = invoker.toolprefix
+  } else {
+    toolprefix = ""
+  }
+
+  gcc_toolchain(target_name) {
+    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+    cc = "$prefix/clang"
+    cxx = "$prefix/clang++"
+    ld = cxx
+    readelf = "${toolprefix}readelf"
+    ar = "${prefix}/llvm-ar"
+    nm = "${toolprefix}nm"
+
+    forward_variables_from(invoker,
+                           [
+                             "strip",
+                             "is_clang_analysis_supported",
+                             "shlib_extension",
+                           ])
+
+    toolchain_args = {
+      if (defined(invoker.toolchain_args)) {
+        forward_variables_from(invoker.toolchain_args, "*")
+      }
+    }
+  }
+}
diff --git a/src/starboard/build/toolchain/get_clang_base_path.py b/src/starboard/build/toolchain/get_clang_base_path.py
new file mode 100755
index 0000000..d6fc75a
--- /dev/null
+++ b/src/starboard/build/toolchain/get_clang_base_path.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Finds and prints the Clang base path.
+
+If Clang is not present, it will be downloaded.
+"""
+
+import os.path
+import sys
+
+sys.path.append(
+    os.path.realpath(
+        os.path.join(
+            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)))
+from cobalt.build.gyp_utils import EnsureClangAvailable  # pylint: disable=g-import-not-at-top
+from cobalt.build.gyp_utils import GetClangBasePath
+from cobalt.build.gyp_utils import GetClangBinPath
+
+
+def main():
+  base_dir = GetClangBasePath()
+  bin_path = GetClangBinPath()
+  EnsureClangAvailable(base_dir, bin_path)
+  print os.path.join(base_dir, 'llvm-build', 'Release+Asserts')
+
+if __name__ == '__main__':
+  main()
diff --git a/src/starboard/build/toolchain/get_concurrent_links.py b/src/starboard/build/toolchain/get_concurrent_links.py
new file mode 100644
index 0000000..be5640a
--- /dev/null
+++ b/src/starboard/build/toolchain/get_concurrent_links.py
@@ -0,0 +1,86 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Computes and prints the default number of concurrent links.
+
+Computes the number of concurrent links to be run in the build, as a function
+of machine spec. Based on GetDefaultConcurrentLinks in GYP.
+"""
+
+import ctypes
+import optparse
+import os
+import re
+import subprocess
+import sys
+
+
+def _GetTotalMemoryInBytes():
+  """Gets the total amount of virtual memory in bytes.
+
+  Returns:
+    See above
+  """
+  if sys.platform in ('win32', 'cygwin'):
+
+    class MEMORYSTATUSEX(ctypes.Structure):
+      _fields_ = [
+          ('dwLength', ctypes.c_ulong),
+          ('dwMemoryLoad', ctypes.c_ulong),
+          ('ullTotalPhys', ctypes.c_ulonglong),
+          ('ullAvailPhys', ctypes.c_ulonglong),
+          ('ullTotalPageFile', ctypes.c_ulonglong),
+          ('ullAvailPageFile', ctypes.c_ulonglong),
+          ('ullTotalVirtual', ctypes.c_ulonglong),
+          ('ullAvailVirtual', ctypes.c_ulonglong),
+          ('sullAvailExtendedVirtual', ctypes.c_ulonglong),
+      ]
+
+    stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX))
+    ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
+    return stat.ullTotalPhys
+  elif sys.platform.startswith('linux'):
+    if os.path.exists('/proc/meminfo'):
+      with open('/proc/meminfo') as meminfo:
+        memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
+        for line in meminfo:
+          match = memtotal_re.match(line)
+          if not match:
+            continue
+          return float(match.group(1)) * 2**10
+  elif sys.platform == 'darwin':
+    try:
+      return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
+    except subprocess.CalledProcessError:
+      return 0
+  # TODO: Implement this for other platforms.
+  return 0
+
+
+def _GetDefaultConcurrentLinks(mem_per_link_gb, reserve_mem_gb):
+  # Inherit the legacy environment variable for people that have set it in GYP.
+  pool_size = int(os.getenv('GYP_LINK_CONCURRENCY', 0))
+  if pool_size:
+    return pool_size
+
+  mem_total_bytes = _GetTotalMemoryInBytes()
+  mem_total_bytes = max(0, mem_total_bytes - reserve_mem_gb * 2**30)
+  num_concurrent_links = int(max(1, mem_total_bytes / mem_per_link_gb / 2**30))
+  hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
+  return min(num_concurrent_links, hard_cap)
+
+
+def main():
+  parser = optparse.OptionParser()
+  parser.add_option('--mem_per_link_gb', action='store', type='int', default=6)
+  parser.add_option('--reserve_mem_gb', action='store', type='int', default=0)
+  parser.disable_interspersed_args()
+  options, _ = parser.parse_args()
+
+  print _GetDefaultConcurrentLinks(options.mem_per_link_gb,
+                                   options.reserve_mem_gb)
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/build/toolchain/goma.gni b/src/starboard/build/toolchain/goma.gni
new file mode 100644
index 0000000..dd72cefe
--- /dev/null
+++ b/src/starboard/build/toolchain/goma.gni
@@ -0,0 +1,40 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Defines the configuration of Goma.
+
+# Allow ports to set an alternate default for use_goma
+import("//$starboard_path/configuration.gni")
+
+declare_args() {
+  if (!defined(use_goma)) {
+    # Set to true to enable distributed compilation using Goma. By default we
+    # use Goma for stub and linux.
+    use_goma = false
+  }
+
+  # Set the default value based on the platform.
+  if (host_os == "win" || host_os == "winrt_81" ||
+      host_os == "winrt_81_phone" || host_os == "winrt_10") {
+    # Absolute directory containing the gomacc.exe binary.
+    goma_dir = "C:\goma\goma-win64"
+  } else {
+    # Absolute directory containing the gomacc binary.
+    goma_dir = getenv("HOME") + "/goma"
+  }
+}
diff --git a/src/starboard/build/toolchain/linux/BUILD.gn b/src/starboard/build/toolchain/linux/BUILD.gn
new file mode 100644
index 0000000..962109a
--- /dev/null
+++ b/src/starboard/build/toolchain/linux/BUILD.gn
@@ -0,0 +1,217 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Modifications Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/toolchain/gcc_toolchain.gni")
+
+clang_toolchain("clang_arm") {
+  toolprefix = "arm-linux-gnueabihf-"
+  shlib_extension = ".so"
+  toolchain_args = {
+    current_cpu = "arm"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_arm64") {
+  toolprefix = "aarch64-linux-gnu-"
+  shlib_extension = ".so"
+  toolchain_args = {
+    current_cpu = "arm64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("arm64") {
+  toolprefix = "aarch64-linux-gnu-"
+  shlib_extension = ".so"
+
+  cc = "${toolprefix}gcc"
+  cxx = "${toolprefix}g++"
+
+  ar = "${toolprefix}ar"
+  ld = cxx
+  readelf = "${toolprefix}readelf"
+  nm = "${toolprefix}nm"
+
+  toolchain_args = {
+    current_cpu = "arm64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("arm") {
+  toolprefix = "arm-linux-gnueabihf-"
+  shlib_extension = ".so"
+
+  cc = "${toolprefix}gcc"
+  cxx = "${toolprefix}g++"
+
+  ar = "${toolprefix}ar"
+  ld = cxx
+  readelf = "${toolprefix}readelf"
+  nm = "${toolprefix}nm"
+
+  toolchain_args = {
+    current_cpu = "arm"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_x86") {
+  shlib_extension = ".so"
+
+  toolchain_args = {
+    current_cpu = "x86"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("x86") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "x86"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_x64") {
+  shlib_extension = ".so"
+
+  toolchain_args = {
+    current_cpu = "x64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("x64") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "x64"
+    current_os = "linux"
+  }
+}
+
+clang_toolchain("clang_mipsel") {
+  shlib_extension = ".so"
+  toolchain_args = {
+    current_cpu = "mipsel"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("mipsel") {
+  shlib_extension = ".so"
+
+  cc = "mipsel-linux-gnu-gcc"
+  cxx = "mipsel-linux-gnu-g++"
+  ar = "mipsel-linux-gnu-ar"
+  ld = cxx
+  readelf = "mipsel-linux-gnu-readelf"
+  nm = "mipsel-linux-gnu-nm"
+
+  toolchain_args = {
+    current_cpu = "mipsel"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("s390x") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "s390x"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("ppc64") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "ppc64"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("mips") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "mips"
+    current_os = "linux"
+  }
+}
+
+gcc_toolchain("mips64") {
+  shlib_extension = ".so"
+
+  cc = "gcc"
+  cxx = "g++"
+
+  readelf = "readelf"
+  nm = "nm"
+  ar = "ar"
+  ld = cxx
+
+  toolchain_args = {
+    current_cpu = "mips64"
+    current_os = "linux"
+  }
+}
diff --git a/src/starboard/build/toolchain/linux/config/BUILD.gn b/src/starboard/build/toolchain/linux/config/BUILD.gn
new file mode 100644
index 0000000..63f8da6
--- /dev/null
+++ b/src/starboard/build/toolchain/linux/config/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Sets the optimization level
+config("no_optimizations") {
+  cflags = [ "-O0" ]
+}
+
+config("debuggable_optimizations") {
+  cflags = [ "-O2" ]
+}
+
+config("full_optimizations") {
+  cflags = [ "-O3" ]
+}
+
+
+# Enables/disables RTTI
+config("rtti") {
+  cflags_cc = [ "-frtti" ]
+}
+
+config("no_rtti") {
+  cflags_cc = [ "-fno-rtti" ]
+}
+
+
+# Enables -Wexit-time-destructors. Only usable for Clang.
+config("wexit_time_destructors") {
+  cflags = [ "-Wexit-time-destructors" ]
+}
diff --git a/src/starboard/build/toolchain/wrapper_utils.py b/src/starboard/build/toolchain/wrapper_utils.py
new file mode 100644
index 0000000..d01933d
--- /dev/null
+++ b/src/starboard/build/toolchain/wrapper_utils.py
@@ -0,0 +1,150 @@
+# Copyright (c) 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Helper functions for gcc_toolchain.gni wrappers."""
+
+import gzip
+import os
+import re
+import shlex
+import shutil
+import subprocess
+import threading
+
+_BAT_PREFIX = 'cmd /c call '
+_WHITELIST_RE = re.compile('whitelisted_resource_(?P<resource_id>[0-9]+)')
+
+
+def _GzipThenDelete(src_path, dest_path):
+  # Results for Android map file with GCC on a z620:
+  # Uncompressed: 207MB
+  # gzip -9: 16.4MB, takes 8.7 seconds.
+  # gzip -1: 21.8MB, takes 2.0 seconds.
+  # Piping directly from the linker via -print-map (or via -Map with a fifo)
+  # adds a whopping 30-45 seconds!
+  with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path, 'wb', 1) as f_out:
+    shutil.copyfileobj(f_in, f_out)
+  os.unlink(src_path)
+
+
+def CommandToRun(command):
+  """Generates commands compatible with Windows.
+
+  When running on a Windows host and using a toolchain whose tools are
+  actually wrapper scripts (i.e. .bat files on Windows) rather than binary
+  executables, the |command| to run has to be prefixed with this magic.
+  The GN toolchain definitions take care of that for when GN/Ninja is
+  running the tool directly.  When that command is passed in to this
+  script, it appears as a unitary string but needs to be split up so that
+  just 'cmd' is the actual command given to Python's subprocess module.
+
+  Args:
+    command: List containing the UNIX style |command|.
+
+  Returns:
+    A list containing the Windows version of the |command|.
+  """
+  if command[0].startswith(_BAT_PREFIX):
+    command = command[0].split(None, 3) + command[1:]
+  return command
+
+
+def RunLinkWithOptionalMapFile(command, env=None, map_file=None):
+  """Runs the given command, adding in -Wl,-Map when |map_file| is given.
+
+  Also takes care of gzipping when |map_file| ends with .gz.
+
+  Args:
+    command: List of arguments comprising the command.
+    env: Environment variables.
+    map_file: Path to output map_file.
+
+  Returns:
+    The exit code of running |command|.
+  """
+  tmp_map_path = None
+  if map_file and map_file.endswith('.gz'):
+    tmp_map_path = map_file + '.tmp'
+    command.append('-Wl,-Map,' + tmp_map_path)
+  elif map_file:
+    command.append('-Wl,-Map,' + map_file)
+
+  result = subprocess.call(command, env=env)
+
+  if tmp_map_path and result == 0:
+    threading.Thread(
+        target=lambda: _GzipThenDelete(tmp_map_path, map_file)).start()
+  elif tmp_map_path and os.path.exists(tmp_map_path):
+    os.unlink(tmp_map_path)
+
+  return result
+
+
+def ResolveRspLinks(inputs):
+  """Return a list of files contained in a response file.
+
+  Args:
+    inputs: A command containing rsp files.
+
+  Returns:
+    A set containing the rsp file content.
+  """
+  rspfiles = [a[1:] for a in inputs if a.startswith('@')]
+  resolved = set()
+  for rspfile in rspfiles:
+    with open(rspfile, 'r') as f:
+      resolved.update(shlex.split(f.read()))
+
+  return resolved
+
+
+def CombineResourceWhitelists(whitelist_candidates, outfile):
+  """Combines all whitelists for a resource file into a single whitelist.
+
+  Args:
+    whitelist_candidates: List of paths to rsp files containing all targets.
+    outfile: Path to save the combined whitelist.
+  """
+  whitelists = ('%s.whitelist' % candidate for candidate in whitelist_candidates
+                if os.path.exists('%s.whitelist' % candidate))
+
+  resources = set()
+  for whitelist in whitelists:
+    with open(whitelist, 'r') as f:
+      resources.update(f.readlines())
+
+  with open(outfile, 'w') as f:
+    f.writelines(resources)
+
+
+def ExtractResourceIdsFromPragmaWarnings(text):
+  """Returns set of resource IDs that are inside unknown pragma warnings.
+
+  Args:
+    text: The text that will be scanned for unknown pragma warnings.
+
+  Returns:
+    A set containing integers representing resource IDs.
+  """
+  used_resources = set()
+  lines = text.splitlines()
+  for ln in lines:
+    match = _WHITELIST_RE.search(ln)
+    if match:
+      resource_id = int(match.group('resource_id'))
+      used_resources.add(resource_id)
+
+  return used_resources
+
+
+def CaptureCommandStderr(command, env=None):
+  """Returns the stderr of a command.
+
+  Args:
+    command: A list containing the command and arguments.
+    env: Environment variables for the new process.
+  """
+  child = subprocess.Popen(command, stderr=subprocess.PIPE, env=env)
+  _, stderr = child.communicate()
+  return child.returncode, stderr
diff --git a/src/starboard/byte_swap.h b/src/starboard/byte_swap.h
index 9fbd1fa..2347bd2 100644
--- a/src/starboard/byte_swap.h
+++ b/src/starboard/byte_swap.h
@@ -82,4 +82,47 @@
 }  // extern "C"
 #endif
 
+#ifdef __cplusplus
+
+#include <algorithm>
+
+template <typename T>
+T SbByteSwap(T value) {
+  std::reverse(reinterpret_cast<uint8_t*>(&value),
+               reinterpret_cast<uint8_t*>(&value + 1));
+  return value;
+}
+
+template <>
+inline int16_t SbByteSwap(int16_t value) {
+  return SbByteSwapS16(value);
+}
+
+template <>
+inline uint16_t SbByteSwap(uint16_t value) {
+  return SbByteSwapU16(value);
+}
+
+template <>
+inline int32_t SbByteSwap(int32_t value) {
+  return SbByteSwapS32(value);
+}
+
+template <>
+inline uint32_t SbByteSwap(uint32_t value) {
+  return SbByteSwapU32(value);
+}
+
+template <>
+inline int64_t SbByteSwap(int64_t value) {
+  return SbByteSwapS64(value);
+}
+
+template <>
+inline uint64_t SbByteSwap(uint64_t value) {
+  return SbByteSwapU64(value);
+}
+
+#endif
+
 #endif  // STARBOARD_BYTE_SWAP_H_
diff --git a/src/starboard/common/BUILD.gn b/src/starboard/common/BUILD.gn
new file mode 100644
index 0000000..6d9fcbd
--- /dev/null
+++ b/src/starboard/common/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: change to //starboard eventually
+import("//cobalt/build/config/base.gni")
+
+static_library("common") {
+  sources = [
+    "common.cc",
+    "flat_map.h",
+    "memory.cc",
+    "move.h",
+    "new.cc",
+    "optional.cc",
+    "ref_counted.cc",
+    "ref_counted.h",
+    "reset_and_return.h",
+    "rwlock.h",
+    "semaphore.h",
+    "scoped_ptr.h",
+    "state_machine.cc",
+    "state_machine.h",
+    "thread_collision_warner.cc",
+    "thread_collision_warner.h",
+  ]
+
+  # TODO: get rid of dependence on Cobalt
+  if (!enable_custom_media_session_client) {
+    sources += [ "//starboard/shared/media_session/stub_playback_state.cc" ]
+  }
+
+  # This must be defined when building Starboard, and must not when building
+  # Starboard client code.
+  defines = [ "STARBOARD_IMPLEMENTATION" ]
+}
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index bf285ef..f85fbb5 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -29,12 +29,15 @@
         'memory.cc',
         'move.h',
         'new.cc',
+        'optional.cc',
         'ref_counted.cc',
         'ref_counted.h',
         'reset_and_return.h',
         'rwlock.h',
         'semaphore.h',
         'scoped_ptr.h',
+        'state_machine.cc',
+        'state_machine.h',
         'thread_collision_warner.cc',
         'thread_collision_warner.h',
       ],
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/common/optional.cc
similarity index 69%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/common/optional.cc
index be4e805..f9618d1 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/common/optional.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
+// Copyright 2014 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#include "starboard/common/optional.h"
 
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+namespace starboard {
+const nullopt_t nullopt;
+const in_place_t in_place;
+}  // namespace starboard
diff --git a/src/starboard/common/optional.h b/src/starboard/common/optional.h
new file mode 100644
index 0000000..8e8fc00
--- /dev/null
+++ b/src/starboard/common/optional.h
@@ -0,0 +1,428 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_COMMON_OPTIONAL_H_
+#define STARBOARD_COMMON_OPTIONAL_H_
+
+#include <algorithm>
+#include <iosfwd>
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+
+// This class is based off of std::experimental::optional:
+//   http://en.cppreference.com/w/cpp/experimental/optional
+//
+// It is a template class where instances parameterized by type T contain
+// memory for an instance of type T, but it may or may not be constructed.
+// If it is not constructed, it cannot be accessed, and if it is, it can
+// be accessed.  This allows one to check if the inner object exists or not
+// before using it, and is useful for functions that may or may not return
+// a value.  Note that the memory for the object is stored internally, so
+// no heap allocations are made over the course of construction and destruction
+// of the internal object (unless the internal object allocates memory within
+// its constructor).
+//
+// Some functionality is left out.  For example, most C++11 functionality
+// is not implemented, since we would like this to be friendly to non-C++11
+// compilers.
+//
+// In the future, if C++11 functionality is needed, it can be implemented
+// and surrounded by preprocessor guards to maintain compatibility with non
+// C++11 compilers.
+//
+
+// The nullopt_t type is used as a signal for an empty optional.  If any
+// optional is assigned the value of nullopt, it will be disengaged.
+// For example,
+//   starboard::optional<int> my_int_optional(5);
+//   EXPECT_FALSE(!my_int_optional);
+//   my_int_optional = starboard::nullopt;
+//   EXPECT_TRUE(!my_int_optional);
+//
+struct nullopt_t {
+  nullopt_t() {}
+};
+extern const nullopt_t nullopt;
+
+// The in_place_t type is used to signal in-place construction of the internal
+// object.  This is used by the in place constructor of optional, which forwards
+// its parameters to the internal object's constructor.
+// For example,
+//   class Foo {
+//    public:
+//     Foo(int x, int y) { x_ = x; y_ = y; }
+//     int x() const { return x_; }
+//     int y() const { return y_; }
+//
+//    private:
+//     int x_;
+//     int y_;
+//   };
+//
+//   ...
+//
+//   base::optional<Foo> my_foo(base::in_place, 2, 3);
+//   EXPECT_FALSE(!my_foo);
+//   EXPECT_EQ(2, my_foo->x());
+//   EXPECT_EQ(3, my_foo->y());
+//
+struct in_place_t {
+  in_place_t() {}
+};
+extern const in_place_t in_place;
+
+template <typename T>
+class SB_EXPORT optional {
+ public:
+  // Construction via the default constructor results in an optional that is
+  // not engaged.
+  optional() { InitializeAsDisengaged(); }
+
+  optional(nullopt_t) { InitializeAsDisengaged(); }  // NOLINT(runtime/explicit)
+
+  // This non-explicit singleton constructor is provided so users can pass in a
+  // T wherever a optional<T> is expected.
+  optional(const T& value) { SetValue(value); }  // NOLINT(runtime/explicit)
+
+  optional(const optional<T>& other) {  // NOLINT(runtime/explicit)
+    if (other.engaged_) {
+      SetValue(other.value());
+    } else {
+      InitializeAsDisengaged();
+    }
+  }
+
+  // Move constructor.
+  // NOLINTNEXTLINE(runtime/explicit)
+  optional(optional&& other) {  // NOLINT(build/c++11)
+    if (other.engaged_) {
+      SetValue(std::move(other.value()));
+    } else {
+      InitializeAsDisengaged();
+    }
+  }
+
+  // Move value constructor.
+  // NOLINTNEXTLINE(runtime/explicit)
+  optional(T&& other) {  // NOLINT(build/c++11)
+    SetValue(std::move(other));
+  }
+
+  // Destruct contained object upon optional's destruction.
+  ~optional() { EnsureDisengaged(); }
+
+  // Disengages the optional, calling the destructor of the contained object
+  // if it is engaged.
+  optional<T>& operator=(nullopt_t) {
+    EnsureDisengaged();
+    return *this;
+  }
+
+  // Reassigns the underlying optional to value passed in on the right hand
+  // side.  This will destruct the lhs contained object first if it exists.
+  template <typename U>
+  optional<T>& operator=(const U& other) {
+    if (engaged_) {
+      value() = other;
+    } else {
+      SetValue(other);
+    }
+    return *this;
+  }
+
+  // Copy assignment.
+  optional<T>& operator=(const optional<T>& other) {
+    if (engaged_ && other.engaged_) {
+      value() = other.value();
+    } else if (!engaged_ && other.engaged_) {
+      SetValue(other.value());
+    } else if (engaged_ && !other.engaged_) {
+      EnsureDisengaged();
+    }
+    // Do nothing if lhs and rhs are both not engaged.
+    return *this;
+  }
+
+  // Move value assignment.
+  optional<T>& operator=(T&& other) {  // NOLINT(build/c++11)
+    if (engaged_) {
+      value() = std::move(other);
+    } else {
+      SetValue(std::move(other));
+    }
+    return *this;
+  }
+
+  // Move optional assignment.
+  optional<T>& operator=(optional&& other) {  // NOLINT(build/c++11)
+    if (engaged_ && other.engaged_) {
+      value() = std::move(other.value());
+    } else if (!engaged_ && other.engaged_) {
+      SetValue(std::move(other.value()));
+    } else if (engaged_ && !other.engaged_) {
+      EnsureDisengaged();
+    }
+    // Do nothing if lhs and rhs are both not engaged.
+    return *this;
+  }
+
+// Overloaded conversion to bool operator for determining whether the optional
+// is engaged or not.  It returns true if the optional is engaged, and false
+// otherwise.
+#if (defined(_MSC_VER) && (_MSC_VER < 1800)) || \
+    (defined(__GNUC__) &&                       \
+     (__GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 5))))
+  // MSVC 2012 does not support explicit cast operators.
+  // http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
+
+  // For any compiler that doesn't support explicit bool operators, we instead
+  // use the Safe Bool Idiom: http://www.artima.com/cppsource/safebool.html
+ private:
+  // The type of SafeBoolIdiomType (pointer to data member of a private type) is
+  // limited in functionality so much that the only thing a user can do with it
+  // is test for null, or apply to operator==/operator!=.  Since both operators
+  // == and != are already overloaded for optional, this leaves null tests,
+  // which we use for boolean testing.
+  class PrivateSafeBoolIdiomFakeMemberType;
+  typedef PrivateSafeBoolIdiomFakeMemberType optional::*SafeBoolIdiomType;
+
+ public:
+  operator const SafeBoolIdiomType() const {
+    // If we wish to return true, we cast engaged_ to our private type giving
+    // a non-null pointer to data member.  Otherwise, we return NULL.  The
+    // only thing the user can do with the return type is test for NULL.
+    return engaged_
+               ? reinterpret_cast<const SafeBoolIdiomType>(&optional::engaged_)
+               : NULL;
+  }
+#else
+  explicit operator bool() const { return engaged_; }
+#endif
+
+  // Dereferences the internal object.
+  const T* operator->() const { return &(value()); }
+
+  T* operator->() { return &(value()); }
+
+  const T& operator*() const { return value(); }
+
+  T& operator*() { return value(); }
+
+  // Dereferences and returns the internal object.
+  const T& value() const {
+    SB_DCHECK(engaged_)
+        << "Attempted to access object in a disengaged optional.";
+    return *static_cast<const T*>(void_value());
+  }
+
+  T& value() {
+    SB_DCHECK(engaged_)
+        << "Attempted to access object in a disengaged optional.";
+    return *static_cast<T*>(void_value());
+  }
+
+  template <typename U>
+  T value_or(const U& value) const {
+    if (engaged_) {
+      return this->value();
+    } else {
+      return value;
+    }
+  }
+
+  // Swaps the values of two optionals.
+  void swap(optional<T>& other) {
+    if (engaged_ && other.engaged_) {
+      // Swap the value contents with each other.
+      std::swap(value(), other.value());
+    } else if (engaged_) {
+      other.SetValue(std::move(value()));
+      EnsureDisengaged();
+    } else if (other.engaged_) {
+      SetValue(std::move(other.value()));
+      other.EnsureDisengaged();
+    }
+    // If both the lhs and rhs are not engaged, we do nothing.
+  }
+
+// Include the pump.py-generated declaration and implementation for the
+// forwarding constructor and emplace.
+#include "starboard/common/optional_internal.h"
+
+ private:
+  // Sets a non-engaged optional to a specified value, and marks it as engaged.
+  void SetValue(T&& value) {  // NOLINT(build/c++11)
+    new (void_value()) T(std::move(value));
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+  // Sets a non-engaged optional to a specified value, and marks it as engaged.
+  template <typename U>
+  void SetValue(const U& value) {
+    new (void_value()) T(value);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+  // If an optional is engaged, it destructs the wrapped value and marks the
+  // optional as disengaged.  Does nothing to a disengaged optional.
+  void EnsureDisengaged() {
+    if (engaged_) {
+      static_cast<T*>(void_value())->~T();
+      engaged_ = false;
+#if !defined(NDEBUG)
+      value_ptr_ = NULL;
+#endif
+    }
+  }
+
+  // Called upon object construction to initialize the object into a disengaged
+  // state.
+  void InitializeAsDisengaged() {
+    engaged_ = false;
+#if !defined(NDEBUG)
+    value_ptr_ = NULL;
+#endif
+  }
+
+  const void* void_value() const {
+    return reinterpret_cast<const void*>(value_memory_);
+  }
+  void* void_value() { return reinterpret_cast<void*>(value_memory_); }
+
+  // The actual memory reserved for the object that may or may not exist.
+  SB_ALIGNAS(SB_ALIGNOF(T)) uint8_t value_memory_[sizeof(T)];
+  // This boolean tracks whether or not the object is constructed yet or not.
+  bool engaged_;
+#if !defined(NDEBUG)
+  // In debug builds, this member makes it easy to inspect the value contained
+  // in the optional via a debugger.
+  const T* value_ptr_;
+#endif
+};
+
+// Comparison between 2 optionals
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const optional<T>& rhs) {
+  if (!lhs) {
+    return !rhs;
+  }
+
+  return rhs == lhs.value();
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const optional<T>& rhs) {
+  if (lhs && rhs) {
+    return lhs.value() < rhs.value();
+  } else {
+    // Handle all other cases simply in terms of whether the optionals are
+    // engaged or not.
+    return static_cast<bool>(lhs) < static_cast<bool>(rhs);
+  }
+}
+
+// Comparison with nullopt_t
+template <typename T>
+inline bool operator==(nullopt_t, const optional<T>& rhs) {
+  return !rhs;
+}
+
+template <typename T>
+inline bool operator==(const optional<T>& lhs, nullopt_t rhs) {
+  return rhs == lhs;
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& /* lhs */, nullopt_t) {
+  return false;
+}
+
+template <typename T>
+inline bool operator<(nullopt_t, const optional<T>& rhs) {
+  return static_cast<bool>(rhs);
+}
+
+// Comparison between an optional and a value
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const T& rhs) {
+  return (!lhs ? false : lhs.value() == rhs);
+}
+
+template <typename T>
+inline bool operator==(const T& lhs, const optional<T>& rhs) {
+  return rhs == lhs;
+}
+
+template <typename T>
+inline bool operator<(const T& lhs, const optional<T>& rhs) {
+  return rhs && lhs < rhs.value();
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const T& rhs) {
+  return !lhs || lhs.value() < rhs;
+}
+
+// This is a convenient but non-standard method, do not rely on it if you expect
+// the compatibility with upcoming C++ versions.
+template <typename T>
+inline std::ostream& operator<<(std::ostream& stream,
+                                const optional<T>& maybe_value) {
+  if (maybe_value) {
+    stream << *maybe_value;
+  } else {
+    stream << "nullopt";
+  }
+  return stream;
+}
+
+template <typename T>
+optional<T> make_optional(const T& value) {
+  return optional<T>(value);
+}
+
+}  // namespace starboard
+
+namespace std {
+
+template <typename T>
+struct hash<::starboard::optional<T> > {
+ public:
+  size_t operator()(const ::starboard::optional<T>& value) const {
+    return (value ? value_hash_(value.value()) : 0);
+  }
+
+ private:
+  hash<T> value_hash_;
+};
+
+template <typename T>
+void swap(::starboard::optional<T>& lhs, ::starboard::optional<T>& rhs) {
+  lhs.swap(rhs);
+}
+
+}  // namespace std
+
+#endif  // STARBOARD_COMMON_OPTIONAL_H_
diff --git a/src/starboard/common/optional_internal.h b/src/starboard/common/optional_internal.h
new file mode 100644
index 0000000..355e1db
--- /dev/null
+++ b/src/starboard/common/optional_internal.h
@@ -0,0 +1,180 @@
+// This file was GENERATED by command:
+//     pump.py optional_internal.h.pump
+// DO NOT EDIT BY HAND!!!
+
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// clang-format off
+// Begin forwarding constructor definitions ////////////////////////////////////
+  explicit optional(in_place_t) {
+    InitializeAsDisengaged();
+    new (void_value()) T();
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1>
+  explicit optional(in_place_t, const P1& p1) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2>
+  explicit optional(in_place_t, const P1& p1, const P2& p2) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4, const P5& p5) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4, const P5& p5, const P6& p6) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6, typename P7>
+  explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+      const P4& p4, const P5& p5, const P6& p6, const P7& p7) {
+    InitializeAsDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6, p7);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+// End forwarding constructor definitions //////////////////////////////////////
+
+// Begin emplace(...) definitions //////////////////////////////////////////////
+  void emplace() {
+    EnsureDisengaged();
+    new (void_value()) T();
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1>
+  void emplace(const P1& p1) {
+    EnsureDisengaged();
+    new (void_value()) T(p1);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2>
+  void emplace(const P1& p1, const P2& p2) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3>
+  void emplace(const P1& p1, const P2& p2, const P3& p3) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+      const P5& p5) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+      const P5& p5, const P6& p6) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+  template <typename P1, typename P2, typename P3, typename P4, typename P5,
+      typename P6, typename P7>
+  void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+      const P5& p5, const P6& p6, const P7& p7) {
+    EnsureDisengaged();
+    new (void_value()) T(p1, p2, p3, p4, p5, p6, p7);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+// End emplace(...) definitions ////////////////////////////////////////////////
+// clang-format on
diff --git a/src/starboard/common/optional_internal.h.pump b/src/starboard/common/optional_internal.h.pump
new file mode 100644
index 0000000..aaf152a
--- /dev/null
+++ b/src/starboard/common/optional_internal.h.pump
@@ -0,0 +1,74 @@
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+$$ This is a pump file for generating the interface and implementation of
+$$ optional's forwarding constructor and emplace() method.  Pump is a python
+$$ script that is part of the Google Test suite of utilities.  Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$var MAX_ARITY = 7
+
+$range ARITY 0..MAX_ARITY
+
+// clang-format off
+// Begin forwarding constructor definitions ////////////////////////////////////
+
+$for ARITY [[
+
+$range ARG 1..ARITY
+
+$if ARITY > 0 [[
+  template <$for ARG , [[typename P$(ARG)]]>
+
+]]
+  explicit optional(in_place_t$if ARITY > 0 [[, ]]
+                    $for ARG , [[const P$(ARG)& p$(ARG)]]) {
+    InitializeAsDisengaged();
+    new (void_value()) T($for ARG , [[p$(ARG)]]);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+]]
+// End forwarding constructor definitions //////////////////////////////////////
+
+// Begin emplace(...) definitions //////////////////////////////////////////////
+
+$for ARITY [[
+
+$range ARG 1..ARITY
+
+$if ARITY > 0 [[
+  template <$for ARG , [[typename P$(ARG)]]>
+
+]]
+  void emplace($for ARG , [[const P$(ARG)& p$(ARG)]]) {
+    EnsureDisengaged();
+    new (void_value()) T($for ARG , [[p$(ARG)]]);
+    engaged_ = true;
+#if !defined(NDEBUG)
+    value_ptr_ = static_cast<const T*>(void_value());
+#endif
+  }
+
+]]
+// End emplace(...) definitions ////////////////////////////////////////////////
+// clang-format on
\ No newline at end of file
diff --git a/src/starboard/common/state_machine.cc b/src/starboard/common/state_machine.cc
new file mode 100644
index 0000000..a87e0c7
--- /dev/null
+++ b/src/starboard/common/state_machine.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "starboard/common/state_machine.h"
+
+#include <algorithm>
+
+#include "starboard/log.h"
+
+// --- Macros for common logging idioms ---
+
+#define SM_STATE(s) GetStateString(s) << "(" << s << ")"
+#define SM_STATEN(s) GetStateString(s) << "(" << s.value_or(0) << ")"
+#define SM_EVENT(e) GetEventString(e) << "(" << e << ")"
+#define SM_PREFIX name_ << "-" << SM_STATEN(state_) << "v" << version_ << ": "
+#define SM_DLOG(level) SB_DLOG(level) << SM_PREFIX
+
+namespace starboard {
+
+StateMachineBase::StateMachineBase(const std::string& name)
+    : name_(name),
+      version_(0),
+      is_handling_(false),
+      is_initialized_(false),
+      should_log_(false) {}
+
+void StateMachineBase::Initialize() {
+  if (is_initialized_) {
+    return;
+  }
+
+  if (should_log_) {
+    SM_DLOG(INFO) << "INITIALIZING";
+  }
+  SB_DCHECK(!state_) << SM_PREFIX;
+  SB_DCHECK(!GetParentState(GetUserInitialState())) << SM_PREFIX;
+  is_initialized_ = true;
+  FollowInitialSubstates();
+}
+
+bool StateMachineBase::IsIn(State state) const {
+  optional<State> currentState = state_;
+  while (currentState) {
+    if (state == currentState.value()) {
+      return true;
+    }
+
+    currentState = GetParentState(currentState);
+  }
+
+  return false;
+}
+
+const char* StateMachineBase::GetStateString(optional<State> state) const {
+  if (!state) {
+    return "<none>";
+  }
+
+  const char* user = GetUserStateString(state.value());
+  if (user) {
+    return user;
+  }
+
+  return "UNKNOWN_STATE";
+}
+
+const char* StateMachineBase::GetEventString(optional<Event> event) const {
+  if (!event) {
+    return "<none>";
+  }
+
+  const char* user = GetUserEventString(event.value());
+  if (user) {
+    return user;
+  }
+
+  return "UNKNOWN_EVENT";
+}
+
+void StateMachineBase::Handle(Event event, void* data) {
+  Initialize();
+
+  if (is_handling_) {
+    if (should_log_) {
+      SM_DLOG(INFO) << "QUEUEING " << SM_EVENT(event);
+    }
+
+    event_queue_.push(EventWithData(event, data));
+    return;
+  }
+
+  HandleOneEvent(event, data);
+  HandleQueuedEvents();
+}
+
+optional<StateMachineBase::State> StateMachineBase::GetParentState(
+    optional<State> state) const {
+  if (!state) {
+    return optional<State>();
+  }
+
+  return GetUserParentState(state.value());
+}
+
+optional<StateMachineBase::State> StateMachineBase::GetInitialSubstate(
+    optional<State> state) const {
+  if (!state) {
+    return optional<State>();
+  }
+
+  return GetUserInitialSubstate(state.value());
+}
+
+void StateMachineBase::HandleQueuedEvents() {
+  while (!event_queue_.empty()) {
+    EventWithData& event = event_queue_.front();
+    HandleOneEvent(event.event, event.data);
+    event_queue_.pop();
+  }
+}
+
+void StateMachineBase::HandleOneEvent(Event event, void* data) {
+  if (should_log_) {
+    SM_DLOG(INFO) << "HANDLING " << SM_EVENT(event);
+  }
+
+  SB_DCHECK(!is_handling_) << SM_PREFIX;
+  is_handling_ = true;
+
+  // Walk up the hierarchy from the simplest current state, looking for event
+  // handlers, stopping at the first state that decides to handle the event.
+  optional<State> source = state_;
+  Result result;
+  while (source) {
+    result = HandleUserStateEvent(source.value(), event, data);
+    if (!result.is_handled) {
+      // Result was not handled, so we continue up the state chain.
+      source = GetParentState(source);
+      continue;
+    }
+
+    break;
+  }
+
+  // Log that the event was completely unhandled, because that's kinda weird.
+  // It's better if the state machine handler explicitly consumes and ignores
+  // events that should be ignored.
+  if (!result.is_handled && should_log_) {
+    SM_DLOG(WARNING) << "Event " << SM_EVENT(event) << " was unhandled.";
+  }
+
+  // If a transition was triggered, execute it.
+  if (result.is_transition) {
+    Transition(event, source.value(), result.target.value(),
+               result.is_external);
+  }
+
+  is_handling_ = false;
+}
+
+// Returns the index of |state| in |path|, or -1 if not found in |path_length|
+// elements. If |state| is null, then it will always return -1.
+static int IndexOf(optional<StateMachineBase::State> state,
+                   const StateMachineBase::State* path,
+                   size_t path_length) {
+  if (!state) {
+    return -1;
+  }
+
+  int i;
+  for (i = 0; !(path[i] == state) && i < path_length; ++i) {
+  }
+  return (i == path_length ? -1 : i);
+}
+
+// Finds the least common ancestor between the two state paths. If there is no
+// common ancestor, returns null.
+static optional<StateMachineBase::State> FindLeastCommonAncestor(
+    const StateMachineBase::State* path_a,
+    size_t length_a,
+    const StateMachineBase::State* path_b,
+    size_t length_b) {
+  size_t max = std::min(length_a, length_b);
+  size_t i;
+  for (i = 0; path_a[i] == path_b[i] && i < max; ++i) {
+  }
+  return (i == 0 ? optional<StateMachineBase::State>() : path_a[i - 1]);
+}
+
+void StateMachineBase::GetPath(State state,
+                               size_t max_depth,
+                               State* out_path,
+                               size_t* out_depth) const {
+  if (max_depth == 0) {
+    SM_DLOG(FATAL) << "max_depth must be > 0";
+    if (out_depth) {
+      *out_depth = 0;
+    }
+    return;
+  }
+
+  size_t depth = 0;
+  optional<State> currentState = state;
+  while (currentState && depth < max_depth) {
+    out_path[depth] = currentState.value();
+    ++depth;
+    currentState = GetParentState(currentState);
+  }
+
+  // Reverse so the path is in ascending depth order.
+  std::reverse(out_path, out_path + depth);
+  if (out_depth) {
+    *out_depth = depth;
+  }
+}
+
+void StateMachineBase::Transition(Event event,
+                                  State source,
+                                  State target,
+                                  bool is_external) {
+  if (should_log_) {
+    SM_DLOG(INFO) << SM_EVENT(event) << " caused " << SM_STATE(source) << " -> "
+                  << SM_STATE(target) << (is_external ? " (external)" : "");
+  }
+
+  // Define a reasonable max depth so we don't have to heap allocate. I've never
+  // seen an HSM as deep as whatever arbitrary value this is defined to be at
+  // the moment.
+  static const size_t kMaxDepth = 16;
+  State source_path[kMaxDepth];
+  size_t source_depth;
+  GetPath(source, kMaxDepth, source_path, &source_depth);
+  State target_path[kMaxDepth];
+  size_t target_depth;
+  GetPath(target, kMaxDepth, target_path, &target_depth);
+  optional<State> least_common_ancestor = FindLeastCommonAncestor(
+      source_path, source_depth, target_path, target_depth);
+
+  // External transitions must exit the source state and enter the target
+  // state, so if the LCA is one of the two, we need to go one level up.
+  if (is_external &&
+      (least_common_ancestor == source || least_common_ancestor == target)) {
+    least_common_ancestor = GetParentState(least_common_ancestor);
+  }
+
+  // Unwind (exit) states up to the closest common ancestor.
+  while (state_ && !(state_ == least_common_ancestor)) {
+    ExitCurrentState();
+  }
+
+  // Update version before we wind down to the target.
+  ++version_;
+
+  // Wind (enter) states down to the target state.
+  size_t next_depth = IndexOf(state_, target_path, target_depth) + 1;
+  SB_DCHECK(next_depth <= target_depth);
+  while (next_depth < target_depth) {
+    EnterState(target_path[next_depth]);
+    ++next_depth;
+  }
+
+  FollowInitialSubstates();
+}
+
+void StateMachineBase::FollowInitialSubstates() {
+  while (true) {
+    optional<State> substate =
+        (state_ ? GetInitialSubstate(state_) : GetUserInitialState());
+    if (!substate) {
+      break;
+    }
+    EnterState(substate.value());
+  }
+}
+
+void StateMachineBase::EnterState(State state) {
+  state_ = state;
+
+  if (should_log_) {
+    SM_DLOG(INFO) << "ENTER";
+  }
+
+  HandleUserStateEnter(state);
+}
+
+void StateMachineBase::ExitCurrentState() {
+  if (should_log_) {
+    SM_DLOG(INFO) << "EXIT";
+  }
+
+  HandleUserStateExit(state_.value());
+  state_ = GetParentState(state_);
+}
+
+}  // namespace starboard
diff --git a/src/starboard/common/state_machine.h b/src/starboard/common/state_machine.h
new file mode 100644
index 0000000..017c06d
--- /dev/null
+++ b/src/starboard/common/state_machine.h
@@ -0,0 +1,517 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STARBOARD_COMMON_STATE_MACHINE_H_
+#define STARBOARD_COMMON_STATE_MACHINE_H_
+
+#include <queue>
+#include <string>
+
+#include "starboard/common/optional.h"
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+
+#ifdef __cplusplus
+namespace starboard {
+
+// An approximate implementation of a run-to-completion (RTC) Hierarchical State
+// Machine (HSM), supporting most UML Statechart semantics.
+//
+// Some good background information on UML Statecharts, mostly written by Miro
+// Samek:
+// http://en.wikipedia.org/wiki/UML_state_machine
+//
+// And Miro Samek's 3-part article on practical UML Statecharts:
+// http://www.embedded.com/design/prototyping-and-development/4008247/A-crash-course-in-UML-state-machines-Part-1
+//
+// This class does not provide any intrinsic support for "orthogonal regions,"
+// "extended state," or "deferred events." "Guard conditions" are easily
+// implemented by the client directly in the event handler. It also minorly
+// deviates from the UML Statechart specification by calling transition handlers
+// before exiting the current states. This is because this implementation uses a
+// simplification which combines the transition handlers and the
+// state-event-transition map into a single function (HandleUserStateEvent).
+//
+// This class is not thread-safe. It is the user's responsibility to synchronize
+// calls into the state machine, either with locks or by simply using from a
+// single thread.
+//
+// Terse suggestions for using this class:
+//   * Use the templated StateMachine wrapper class instead of this class
+//     directly.
+//   * Define two enums, one for your states, and one for your events.
+//   * Subclass to define your state machine and event handlers.
+//   * Avoid directly exposing or passing around state machines (wrap instead).
+//     Handle() is not a great public interface.
+//   * Include your state machine in another class as a private by-value member.
+//   * Synchronize access to the state machine.
+//   * Prefer writing state machine event handlers over checking if the machine
+//     IsIn() a particular state.
+//   * Convert public methods into events, get into the state machine as quickly
+//     as possible.
+//   * You only need to define a state when you are going to leave the state
+//     machine in a particular condition where events can occur.
+//   * Create a superstate when you have an event you want to handle the same
+//     way for a collection of states.
+//   * When managing resources, create a state or superstate that represents the
+//     acquisition of that resource, and release the resource in the Exit
+//     handler.
+//
+// Some Definitions:
+//   Simple State      - A State with no substates. The state machine is always
+//                       left in exactly one Simple State.
+//   Composite State   - A State with at least one substate.
+//   Guard Condition   - An external condition on which an event handler
+//                       branches.
+//   Run-To-Completion - A property specifying that the state machine handles
+//                       one event at a time, and no events are handled until
+//                       the previous event is done being handled.
+//
+// See the unittests for this class for a contrived example state machine
+// implementation.
+class StateMachineBase {
+ public:
+  // --- Nested Types and Constants ---
+
+  typedef uint32_t State;
+  typedef uint32_t Event;
+
+  // --- Public Methods ---
+
+  // Constructor with name. The name is helpful for debugging.
+  explicit StateMachineBase(const std::string& name);
+  virtual ~StateMachineBase() {}
+
+  // Enters the initial state, as specified by |GetUserInitialState()| and
+  // follows the initial substates down to the first simple (childless) state
+  // found. Idempotent. Will happen automatically on the first |Handle()| call,
+  // if not done explicitly.
+  void Initialize();
+
+  // Gets the name of this state machine, for logging purposes.
+  const char* name() const { return name_.c_str(); }
+
+  // Gets a version number that increases monotonically with each state
+  // transition (unless it overflows |uint64_t|). This can be useful for timers
+  // and asynchronous events that only want to do their action if the state has
+  // not changed since they were queued.
+  //
+  // The state version changes exactly once per transition. In other words, it
+  // doesn't change for every Enter and Exit for a transition.
+  uint64_t version() const { return version_; }
+
+  // Gets the simplest current state that this state machine is in. To check if
+  // the state machine is in a particular composite state, use |IsIn()|. Returns
+  // null if uninitialized.
+  optional<State> state() const { return state_; }
+
+  // Reports whether the state machine is currently in the given state. A state
+  // machine is considered to be "in" the current simple state, but also "in"
+  // all superstates of the current simple state.
+  bool IsIn(State state) const;
+
+  // Gets a printable string for the given state.
+  const char* GetStateString(optional<State> state) const;
+
+  // Gets a printable string for the given event.
+  const char* GetEventString(optional<Event> event) const;
+
+  // Handles the given event in the context of the state machine's current
+  // state, executing the appropriate event handlers and performing any
+  // precipitate state transitions. If called reentrantly, the event will be
+  // queued until the current event has run to completion.
+  //
+  // |data| is a way to pass auxiliary data to the event handler. No retention
+  // or ref-counting is done on that data, it is simply passed through to the
+  // event handler. Since |Handle()| will queue the event if (and only if)
+  // called from an event handler, it is up to the caller to ensure the lifetime
+  // of whatever is passed in here.
+  void Handle(Event event, void* data = NULL);
+
+ protected:
+  // --- Protected Nested Types ---
+
+  // A type that can be returned from a state-event handler, which will be
+  // coerced into the appropriate Result structure.
+  enum HandledState {
+    // The event handler returns this to say that the handler consume the event,
+    // but caused no state transition.
+    kHandled,
+
+    // The event handler returns this to say that the handler did not consume
+    // the event, and it should bubble up to the parent state, if any.
+    kNotHandled,
+  };
+
+  // Structure that handlers return, allowing them to cause state transitions or
+  // prevent the event from bubbling. State-event handlers may just return a
+  // HandledState or a state to transition to, and it will be coerced into this
+  // structure.
+  struct Result {
+    // Default constructor is unhandled.
+    Result() : is_transition(false), is_external(false), is_handled(false) {}
+
+    // The no-transition constructor. Non-explicit so that the implementor of
+    // |HandleUserStateEvent()| just needs to return a |HandledState| and it
+    // does the right thing.
+    Result(HandledState handled)  // NOLINT(runtime/explicit)
+        : is_transition(false),
+          is_external(false),
+          is_handled(handled == kHandled) {}
+
+    // The state transition constructor. This implies that the event was
+    // handled. Non-explicit so that the implementor of |HandleUserStateEvent()|
+    // just needs to return a State and it does a non-external transition.
+    Result(State transition_target,
+           bool external = false)  // NOLINT(runtime/explicit)
+        : target(transition_target),
+          is_transition(true),
+          is_external(external),
+          is_handled(true) {}
+
+    Result& operator=(HandledState rhs) {
+      target = nullopt;
+      is_transition = false;
+      is_external = false;
+      is_handled = (rhs == kHandled);
+      return *this;
+    }
+
+    Result& operator=(State transition_target) {
+      target = transition_target;
+      is_transition = true;
+      is_external = false;
+      is_handled = true;
+      return *this;
+    }
+
+    // State to transition to. Only valid if is_transition is true.
+    optional<State> target;
+
+    // Whether this result indicates a state transition.
+    bool is_transition;
+
+    // Whether the specified transition is external. Only meaningful if
+    // is_transition is true.
+    //
+    // For more on state transitions, see:
+    // http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
+    bool is_external;
+
+    // Whether the event was handled by the handler. If true, consumes the
+    // event, and prevents bubbling up to superstates. False propagates the
+    // event to superstates.
+    bool is_handled;
+  };
+
+  struct EventWithData {
+    EventWithData(Event event, void* data) : event(event), data(data) {}
+    Event event;
+    void* data;
+  };
+
+  // --- Implementation Interface for Subclasses ---
+
+  // Abstract method for subclasses to define the state hierarchy. It is
+  // recommended that all user-defined states be descendents of a single "top"
+  // state.
+  virtual optional<State> GetUserParentState(State state) const = 0;
+
+  // Abstract method for subclasses to define the initial substate of their
+  // states, which is required for all complex states. If a state is a simple
+  // state (no substates), returns null.
+  virtual optional<State> GetUserInitialSubstate(State state) const = 0;
+
+  // Abstract method for subclasses to define the initial (top) state of their
+  // state machine. This must be a state that has no parent. This state will be
+  // entered upon calling |Initialize()|.
+  virtual State GetUserInitialState() const = 0;
+
+  // Optional method for subclasses to define strings for their states, solely
+  // used for debugging purposes.
+  virtual const char* GetUserStateString(State state) const { return NULL; }
+
+  // Optional method for subclasses to define strings for their events, solely
+  // used for debugging purposes.
+  virtual const char* GetUserEventString(Event event) const { return NULL; }
+
+  // Abstract method for subclasses to define handlers for events in given
+  // states. This method must return either:
+  //   a) a state to transition to, meaning the event was handled and caused
+  //      a transition
+  //   b) |kHandled| meaning the event was handled but no transition occurred
+  //   c) |kNotHandled|, meaning the event should bubble up to the parent state
+  //   d) an explicit Result structure, mainly for external transitions.
+  //
+  // Implementations wishing to catch all unhandled events may do so in their
+  // top state.
+  //
+  // This method is generally implemented as a nested switch statement, with the
+  // outer switch on |state| and the inner switch on |event|. You may want to
+  // break this apart into per-state or per-state-event functions for
+  // readability and maintainability.
+  virtual Result HandleUserStateEvent(State state, Event event, void* data) = 0;
+
+  // Abstract method for subclasses to define state entry behaviors.
+  virtual void HandleUserStateEnter(State state) = 0;
+
+  // Abstract method for subclasses to define state exit behaviors.
+  virtual void HandleUserStateExit(State state) = 0;
+
+  // --- Helper Methods for Subclasses ---
+
+  // Subclasses can call this method to turn on logging. Logging is opt-in,
+  // because it can be very verbose, and is likely only useful during
+  // development of a particular state machine.
+  void EnableLogging() { should_log_ = true; }
+
+ private:
+  // --- Private Helper Methods ---
+
+  // Gets the parent state of the given state.
+  optional<State> GetParentState(optional<State> state) const;
+
+  // Gets the initial substate of given state.
+  optional<State> GetInitialSubstate(optional<State> state) const;
+
+  // Handles all queued events until there are no more to run. Event handlers
+  // may queue more events by calling |Handle()|, and this method will also
+  // process all precipitate events.
+  void HandleQueuedEvents();
+
+  // Workhorse method for handling a single event.
+  void HandleOneEvent(Event event, void* data);
+
+  // Gets the path from the Top state to the given |state|, storing it in the
+  // given |out_path| array, up to |max_depth| entries. If specified,
+  // |out_depth| will be set to the number of valid states that fit in the given
+  // array.
+  void GetPath(State state,
+               size_t max_depth,
+               State* out_path,
+               size_t* out_depth) const;
+
+  // Transitions between the given source and target states, assuming that the
+  // source state is in the current state path to the Top state. The source
+  // state is the state whose handler generated the transition.
+  //
+  // See:
+  // http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
+  void Transition(Event event, State source, State target, bool is_external);
+
+  // Follows the initial substates from the current state until it reaches a
+  // simple state.
+  void FollowInitialSubstates();
+
+  // Enters the given state.
+  void EnterState(State state);
+
+  // Exits the current state to its parent.
+  void ExitCurrentState();
+
+  // --- Members ---
+
+  // The name of this state machine, for debugging purposes.
+  const std::string name_;
+
+  // The current state of this state machine. Null until initialized.
+  optional<State> state_;
+
+  // The unique version of this state machine's state, updated on every
+  // transition.
+  uint64_t version_;
+
+  // Queue of events to handle once the current event is done being
+  // handled. Should always be empty unless |is_handling_| is true.
+  std::queue<EventWithData> event_queue_;
+
+  // Whether this state machine is actively handling an event. Used to detect
+  // reentrant calls to |Handle()|.
+  bool is_handling_;
+
+  // Whether the state machine has been initialized into its initial state
+  // yet. Used to make |Initialize()| idempotent.
+  bool is_initialized_;
+
+  // Whether the state machine should log information about state transitions.
+  bool should_log_;
+};
+
+// A convenience template wrapper for StateMachineBase. See the above class
+// for complete documentation. Basically, you define your states and events as
+// two enums, and then pass them as template args to this template class. Your
+// state machine should then subclass this template class. It then does the work
+// of casting and converting back and forth from your enums to
+// StateMachineBase's numeric State and Event definitions.
+//
+// All the methods in this class, protected and public, match the description
+// and behavioral contracts of the equivalently named method in
+// StateMachineBase.
+template <typename StateEnum, typename EventEnum>
+class StateMachine {
+ public:
+  // --- Nested Types and Constants ---
+
+  explicit StateMachine(const std::string& name) : machine_(this, name) {}
+  virtual ~StateMachine() {}
+
+  void Initialize() { machine_.Initialize(); }
+
+  const char* name() const { return machine_.name(); }
+
+  uint64_t version() const { return machine_.version(); }
+
+  optional<StateEnum> state() const {
+    optional<BaseState> wrappedState = machine_.state();
+    return (wrappedState ? static_cast<StateEnum>(*wrappedState)
+                         : optional<StateEnum>());
+  }
+
+  bool IsIn(StateEnum state) const {
+    return machine_.IsIn(static_cast<BaseState>(state));
+  }
+
+  const char* GetStateString(optional<StateEnum> state) const {
+    return machine_.GetStateString(state ? static_cast<BaseState>(*state)
+                                         : optional<BaseState>());
+  }
+
+  const char* GetEventString(optional<EventEnum> event) const {
+    return machine_.GetEventString(event ? static_cast<BaseEvent>(*event)
+                                         : optional<BaseEvent>());
+  }
+
+  void Handle(EventEnum event, void* data = NULL) {
+    machine_.Handle(static_cast<BaseEvent>(event), data);
+  }
+
+ protected:
+  // See the other HandledState in StateMachineBase.
+  enum HandledState {
+    kHandled,
+    kNotHandled,
+  };
+
+  // See the other Result in StateMachineBase.
+  struct Result {
+    Result(HandledState handled)  // NOLINT(runtime/explicit)
+        : target(),
+          is_transition(false),
+          is_external(false),
+          is_handled(handled == kHandled) {}
+
+    Result(StateEnum transition_target,
+           bool external = false)  // NOLINT(runtime/explicit)
+        : target(transition_target),
+          is_transition(true),
+          is_external(external),
+          is_handled(true) {}
+
+    Result& operator=(HandledState rhs) {
+      target = nullopt;
+      is_transition = false;
+      is_external = false;
+      is_handled = (rhs == kHandled);
+      return *this;
+    }
+
+    Result& operator=(StateEnum transition_target) {
+      target = transition_target;
+      is_transition = true;
+      is_external = false;
+      is_handled = true;
+      return *this;
+    }
+
+    optional<StateEnum> target;
+    bool is_transition;
+    bool is_external;
+    bool is_handled;
+  };
+
+  virtual optional<StateEnum> GetUserParentState(StateEnum state) const = 0;
+  virtual optional<StateEnum> GetUserInitialSubstate(StateEnum state) const = 0;
+  virtual StateEnum GetUserInitialState() const = 0;
+  virtual const char* GetUserStateString(StateEnum state) const { return NULL; }
+  virtual const char* GetUserEventString(EventEnum event) const { return NULL; }
+  virtual Result HandleUserStateEvent(StateEnum state,
+                                      EventEnum event,
+                                      void* data) = 0;
+  virtual void HandleUserStateEnter(StateEnum state) = 0;
+  virtual void HandleUserStateExit(StateEnum state) = 0;
+
+  void EnableLogging() { machine_.EnableLoggingPublic(); }
+
+ private:
+  typedef StateMachineBase::State BaseState;
+  typedef StateMachineBase::Event BaseEvent;
+
+  // Private contained subclass that forwards and adapts all virtual methods
+  // into this class's equivalent virtual methods.
+  class WrappedMachine : public StateMachineBase {
+   public:
+    WrappedMachine(StateMachine<StateEnum, EventEnum>* wrapper,
+                   const std::string& name)
+        : StateMachineBase(name), wrapper_(wrapper) {}
+
+    optional<State> GetUserParentState(State state) const SB_OVERRIDE {
+      optional<StateEnum> result =
+          wrapper_->GetUserParentState(static_cast<StateEnum>(state));
+      return (result ? static_cast<State>(*result) : optional<State>());
+    }
+
+    optional<State> GetUserInitialSubstate(State state) const SB_OVERRIDE {
+      optional<StateEnum> result =
+          wrapper_->GetUserInitialSubstate(static_cast<StateEnum>(state));
+      return (result ? static_cast<State>(*result) : optional<State>());
+    }
+
+    State GetUserInitialState() const SB_OVERRIDE {
+      return static_cast<State>(wrapper_->GetUserInitialState());
+    }
+
+    const char* GetUserStateString(State state) const SB_OVERRIDE {
+      return wrapper_->GetUserStateString(static_cast<StateEnum>(state));
+    }
+
+    const char* GetUserEventString(Event event) const SB_OVERRIDE {
+      return wrapper_->GetUserEventString(static_cast<EventEnum>(event));
+    }
+
+    Result HandleUserStateEvent(State state,
+                                Event event,
+                                void* data) SB_OVERRIDE {
+      typename StateMachine<StateEnum, EventEnum>::Result result =
+          wrapper_->HandleUserStateEvent(static_cast<StateEnum>(state),
+                                         static_cast<EventEnum>(event), data);
+      if (result.is_transition) {
+        return Result(static_cast<State>(*result.target), result.is_external);
+      }
+
+      return result.is_handled ? kHandled : kNotHandled;
+    }
+
+    void HandleUserStateEnter(State state) SB_OVERRIDE {
+      wrapper_->HandleUserStateEnter(static_cast<StateEnum>(state));
+    }
+
+    void HandleUserStateExit(State state) SB_OVERRIDE {
+      wrapper_->HandleUserStateExit(static_cast<StateEnum>(state));
+    }
+
+    // A public exposure of EnableLogging so the wrapper can access it. Since
+    // this class is private to the wrapper, it is the only one who can see it.
+    void EnableLoggingPublic() { EnableLogging(); }
+
+   private:
+    StateMachine<StateEnum, EventEnum>* wrapper_;
+  };
+
+  WrappedMachine machine_;
+};
+
+}  // namespace starboard
+#endif  // __cplusplus
+
+#endif  // STARBOARD_COMMON_STATE_MACHINE_H_
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index d6007c9..cb8d84a 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -64,6 +64,15 @@
 //   //   exposes functionality for my new feature.
 //   #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// SbDecodeTargetInfoPlane's now 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.
+
+#define SB_DECODE_TARGET_PLANE_FORMAT_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 #define SB_POINTER_INPUT_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
@@ -78,6 +87,9 @@
 #define SB_LOW_MEMORY_EVENT_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
 #define SB_PLAYER_WRITE_SAMPLE_EXTRA_CONST_API_VERSION \
   SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION \
+  SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_STORAGE_NAMES_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
 
 // --- Common Detected Features ----------------------------------------------
 
@@ -312,6 +324,32 @@
 #endif  // SB_NORETURN
 #endif  // SB_API_VERSION >= 4
 
+// Specifies the alignment for a class, struct, union, enum, class/struct field,
+// or stack variable.
+#if !defined(SB_ALIGNAS)
+#if SB_IS(COMPILER_GCC)
+#define SB_ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#elif SB_IS(COMPILER_MSVC)
+#define SB_ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#else
+// Fallback to the C++11 form.
+#define SB_ALIGNAS(byte_alignment) alignas(byte_alignment)
+#endif
+#endif  // !defined(SB_ALIGNAS)
+
+// Returns the alignment reqiured for any instance of the type indicated by
+// |type|.
+#if !defined(SB_ALIGNOF)
+#if SB_IS(COMPILER_GCC)
+#define SB_ALIGNOF(type) __alignof__(type)
+#elif SB_IS(COMPILER_MSVC)
+#define SB_ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#else
+// Fallback to the C++11 form.
+#define SB_ALIGNOF(type) alignof(type)
+#endif
+#endif  // !defined(SB_ALIGNOF)
+
 // --- Configuration Audits --------------------------------------------------
 
 #if !defined(SB_API_VERSION)
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index e109b6a..c437c96 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -287,6 +287,16 @@
   // Typically this would be GL_TEXTURE_2D, but some platforms may require
   // that it be set to something else like GL_TEXTURE_EXTERNAL_OES.
   uint32_t gl_texture_target;
+
+#if SB_API_VERSION >= SB_DECODE_TARGET_FORMAT_VERSION
+  // For kSbDecodeTargetFormat2PlaneYUVNV12 planes: the format of the
+  // texture. Usually, for the luma plane, this is either GL_ALPHA or
+  // GL_RED_EXT. For the chroma plane, this is usually GL_LUMINANCE_ALPHA
+  // or GL_RG_EXT.
+  // Ignored for other plane types.
+  uint32_t gl_texture_format;
+#endif  // SB_API_VERSION >= SB_DECODE_TARGET_NV12_R_RG_API_VERSION
+
 #endif               // SB_HAS(BLITTER)
 
   // The width of the texture/surface for this particular plane.
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index dad7d24..cae076d 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -52,6 +52,15 @@
   int32_t encrypted_byte_count;
 } SbDrmSubSampleMapping;
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+typedef struct SbDrmKeyId {
+  // The ID of the license (or key) required to decrypt this sample. For
+  // PlayReady, this is the license GUID in packed little-endian binary form.
+  uint8_t identifier[16];
+  int identifier_size;
+} SbDrmKeyId;
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 // All the optional information needed per sample for encrypted samples.
 typedef struct SbDrmSampleInfo {
   // The Initialization Vector needed to decrypt this sample.
@@ -113,6 +122,20 @@
                                         int session_id_size,
                                         bool succeeded);
 
+// 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
+// passed along.  Any keys not in the list is considered as deleted.
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+typedef void (*SbDrmSessionKeyStatusesChangedFunc)(
+    SbDrmSystem drm_system,
+    void* context,
+    const void* session_id,
+    int session_id_size,
+    int number_of_keys,
+    const SbDrmKeyId* key_ids,
+    const SbDrmKeyStatus* key_statuses);
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 // --- Constants -------------------------------------------------------------
 
 // An invalid SbDrmSystem.
@@ -156,12 +179,25 @@
 // SbDrmGenerateSessionUpdateRequest() is called.
 // |session_updated_callback|: A function that is called every time after
 // SbDrmUpdateSession() is called.
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
+SB_EXPORT SbDrmSystem SbDrmCreateSystem(
+    const char* key_system,
+    void* context,
+    SbDrmSessionUpdateRequestFunc update_request_callback,
+    SbDrmSessionUpdatedFunc session_updated_callback,
+    SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback);
+
+#else  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 SB_EXPORT SbDrmSystem
 SbDrmCreateSystem(const char* key_system,
                   void* context,
                   SbDrmSessionUpdateRequestFunc update_request_callback,
                   SbDrmSessionUpdatedFunc session_updated_callback);
 
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUS_UPDATE_SUPPORT_API_VERSION
+
 // 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.
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 8812c25..52a9d96 100644
--- a/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
+++ b/src/starboard/egl_and_gles/egl_and_gles_angle.gyp
@@ -13,6 +13,9 @@
 # limitations under the License.
 
 {
+  'variables': {
+    'enable_d3d11_feature_level_11%': 0,
+  },
   'targets': [
     {
       'target_name': 'egl_and_gles_implementation',
@@ -27,6 +30,18 @@
           '<(DEPTH)/third_party/angle/include',
         ],
       },
+      'conditions': [
+        # ANGLE supports GLES3 on Windows only if DirectX11 feauture level 11 is
+        # supported.
+        ['target_os=="win" and enable_d3d11_feature_level_11==1', {
+          'direct_dependent_settings': {
+            'defines': [
+              'GLES3_SUPPORTED',
+              'GL_GLEXT_PROTOTYPES',
+            ],
+          },
+        }]
+      ]
     },
   ],
 }
diff --git a/src/starboard/examples/BUILD.gn b/src/starboard/examples/BUILD.gn
new file mode 100644
index 0000000..0012617
--- /dev/null
+++ b/src/starboard/examples/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/base.gni")
+
+group("examples") {
+  deps = [
+    "//starboard/examples/blitter",
+    "//starboard/examples/window",
+  ]
+
+  if (gl_type != "none") {
+    deps += [
+      "//starboard/examples/glclear",
+    ]
+  }
+}
diff --git a/src/starboard/examples/blitter/BUILD.gn b/src/starboard/examples/blitter/BUILD.gn
new file mode 100644
index 0000000..403815f
--- /dev/null
+++ b/src/starboard/examples/blitter/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+final_executable("blitter") {
+  output_name = "starboard_blitter_example"
+
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//starboard",
+  ]
+}
+
+# TODO: add the deploy target if it's desirable
diff --git a/src/starboard/examples/glclear/BUILD.gn b/src/starboard/examples/glclear/BUILD.gn
new file mode 100644
index 0000000..d90f020
--- /dev/null
+++ b/src/starboard/examples/glclear/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+final_executable("glclear") {
+  output_name = "starboard_glclear_example"
+
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//starboard",
+    # TODO: convert this target over
+    # "//starboard/egl_and_gles",
+  ]
+}
+
+# TODO: add the deploy target if it's desirable
diff --git a/src/starboard/examples/window/BUILD.gn b/src/starboard/examples/window/BUILD.gn
new file mode 100644
index 0000000..b8a295a
--- /dev/null
+++ b/src/starboard/examples/window/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+final_executable("window") {
+  output_name = "starboard_window_example"
+
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//starboard",
+  ]
+}
+
+# TODO: add the deploy target if it's desirable
diff --git a/src/starboard/input.h b/src/starboard/input.h
index 0959a00..9c1e2cc 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -173,7 +173,10 @@
   unsigned int key_modifiers;
 
   // The (x, y) coordinates of the persistent cursor controlled by this device.
-  // The value is |0| if this data is not applicable.
+  // The value is |0| if this data is not applicable. For events with type
+  // kSbInputEventTypeMove and device_type kSbInputDeviceTypeGamepad, this field
+  // is interpreted as a stick position with the range [-1, 1], with positive
+  // values for the up and left direction.
   SbInputVector position;
 
   // The relative motion vector of this input. The value is |0| if this data is
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
new file mode 100644
index 0000000..66579d8
--- /dev/null
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -0,0 +1,608 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/base.gni")
+import("//starboard/build/config/fastbuild.gni")
+import("//starboard/build/config/sanitizers.gni")
+import("//starboard/build/toolchain/asan_symbolizer_path.gni")
+import("//starboard/build/delegated_config.gni")
+import("//starboard/linux/shared/clang.gni")
+import("//starboard/linux/shared/dlmalloc.gni")
+
+# =============================================================================
+# DEFAULT COMPILER CONFIGS
+# =============================================================================
+
+config("compiler_defaults") {
+  cflags = [
+     # We'll pretend not to be Linux, but Starboard instead.
+    "-U__linux__",
+  ]
+
+  cflags_c = [
+    # Limit to C99. This allows Linux to be a canary build for any
+    # C11 features that are not supported on some platforms' compilers.
+    "-std=c99",
+  ]
+
+  cflags_cc = [
+    "-std=gnu++11",
+  ]
+
+  libs = [
+    "asound",
+    "avcodec",
+    "avformat",
+    "avresample",
+    "avutil",
+    "pthread",
+    "rt",
+  ]
+
+  ldflags = []
+
+  if (use_dlmalloc_allocator && !use_asan) {
+    ldflags += [
+      "-Wl,--wrap=malloc",
+      "-Wl,--wrap=calloc",
+      "-Wl,--wrap=realloc",
+      "-Wl,--wrap=memalign",
+      "-Wl,--wrap=reallocalign",
+      "-Wl,--wrap=free",
+      "-Wl,--wrap=strdup",
+      "-Wl,--wrap=malloc_usable_size",
+      "-Wl,--wrap=malloc_stats_fast",
+      "-Wl,--wrap=__cxa_demangle",
+    ]
+  }
+
+  defines = [
+    # Cobalt on Linux flag
+    "COBALT_LINUX",
+    "__STDC_FORMAT_MACROS", # so that we get PRI*
+    # Enable GNU extensions to get prototypes like ffsl.
+    "_GNU_SOURCE=1",
+
+    # 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",
+  ]
+
+  if (use_clang) {
+    cflags += [
+      "-Werror",
+      "-fcolor-diagnostics",
+      # Default visibility to hidden, to enable dead stripping.
+      "-fvisibility=hidden",
+      # Warn for implicit type conversions that may change a value.
+      "-Wconversion",
+      "-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",
+      # 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",
+    ]
+
+    ldflags += [ "-fuse-ld=lld" ]
+
+    if (use_asan) {
+      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",
+      ]
+
+      if (asan_symbolizer_path != "") {
+        defines += [
+          "ASAN_SYMBOLIZER_PATH=\"$asan_symbolizer_path\"",
+        ]
+      }
+    }
+
+    if (use_tsan) {
+      cflags += [
+        "-fsanitize=thread",
+        "-fno-omit-frame-pointer",
+      ]
+
+      ldflags += [
+        "-fsanitize-thread",
+      ]
+
+      defines += [
+        "THREAD_SANITIZER",
+      ]
+    }
+  }
+}
+
+config("compiler_defaults_debug") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_devel") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_qa") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+config("compiler_defaults_gold") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+config("compiler_defaults_host") {
+  cflags = [
+    "-O2",
+  ]
+}
+
+# ==============================================================================
+# DELEGATED CONFIGS
+# ==============================================================================
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+  ]
+}
+
+config("no_pedantic_warnings") {
+  cflags = [
+    # "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 about unused function params.
+    "-Wno-unused-parameter",
+    # 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",
+  ]
+}
+
+
+delegated_config("optimizations") {
+  path = "//starboard/build/toolchain/linux/config"
+  prefixes = [ "no", "debuggable", "full" ]
+  generate_default = false
+}
+
+config("default_optimizations") {
+  if (cobalt_config == "debug") {
+    configs = [ ":no_optimizations" ]
+  } else {
+    configs = [ ":debuggable_optimizations" ]
+  }
+}
+
+
+delegated_config("rtti") {
+  path = "//starboard/build/toolchain/linux/config"
+  generate_default = false
+}
+
+config("default_rtti") {
+  if (cobalt_config == "debug" || cobalt_config == "devel") {
+    configs = [ ":rtti" ]
+  } else {
+    configs = [ ":no_rtti" ]
+  }
+}
+
+
+config("wexit_time_destructors") {
+  if (use_clang) {
+    configs =
+        [ "//starboard/build/toolchain/linux/config:wexit_time_destructors" ]
+  }
+}
+
+# =============================================================================
+# starboard_platform TARGET
+# =============================================================================
+
+source_set("starboard_platform") {
+  visibility = [ "//starboard/linux/*" ]
+
+  sources = [
+    "//starboard/linux/shared/atomic_public.h",
+    "//starboard/linux/shared/configuration_public.h",
+    "//starboard/linux/shared/decode_target_get_info.cc",
+    "//starboard/linux/shared/decode_target_internal.cc",
+    "//starboard/linux/shared/decode_target_internal.h",
+    "//starboard/linux/shared/decode_target_release.cc",
+    "//starboard/linux/shared/media_is_video_supported.cc",
+    "//starboard/linux/shared/player_components_impl.cc",
+    "//starboard/linux/shared/system_get_connection_type.cc",
+    "//starboard/linux/shared/system_get_device_type.cc",
+    "//starboard/linux/shared/system_get_path.cc",
+    "//starboard/linux/shared/system_has_capability.cc",
+    "//starboard/shared/alsa/alsa_audio_sink_type.cc",
+    "//starboard/shared/alsa/alsa_audio_sink_type.h",
+    "//starboard/shared/alsa/alsa_util.cc",
+    "//starboard/shared/alsa/alsa_util.h",
+    "//starboard/shared/alsa/audio_sink_get_max_channels.cc",
+    "//starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc",
+    "//starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc",
+    "//starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc",
+    "//starboard/shared/dlmalloc/memory_map.cc",
+    "//starboard/shared/dlmalloc/memory_unmap.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_decoder.h",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_audio_resampler.h",
+    "//starboard/shared/ffmpeg/ffmpeg_common.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_common.h",
+    "//starboard/shared/ffmpeg/ffmpeg_video_decoder.cc",
+    "//starboard/shared/ffmpeg/ffmpeg_video_decoder.h",
+    "//starboard/shared/gcc/atomic_gcc_public.h",
+    "//starboard/shared/iso/character_is_alphanumeric.cc",
+    "//starboard/shared/iso/character_is_digit.cc",
+    "//starboard/shared/iso/character_is_hex_digit.cc",
+    "//starboard/shared/iso/character_is_space.cc",
+    "//starboard/shared/iso/character_is_upper.cc",
+    "//starboard/shared/iso/character_to_lower.cc",
+    "//starboard/shared/iso/character_to_upper.cc",
+    "//starboard/shared/iso/directory_close.cc",
+    "//starboard/shared/iso/directory_get_next.cc",
+    "//starboard/shared/iso/directory_open.cc",
+    "//starboard/shared/iso/double_absolute.cc",
+    "//starboard/shared/iso/double_exponent.cc",
+    "//starboard/shared/iso/double_floor.cc",
+    "//starboard/shared/iso/double_is_finite.cc",
+    "//starboard/shared/iso/double_is_nan.cc",
+    "//starboard/shared/iso/memory_compare.cc",
+    "//starboard/shared/iso/memory_copy.cc",
+    "//starboard/shared/iso/memory_find_byte.cc",
+    "//starboard/shared/iso/memory_move.cc",
+    "//starboard/shared/iso/memory_set.cc",
+    "//starboard/shared/iso/string_compare.cc",
+    "//starboard/shared/iso/string_compare_all.cc",
+    "//starboard/shared/iso/string_find_character.cc",
+    "//starboard/shared/iso/string_find_last_character.cc",
+    "//starboard/shared/iso/string_find_string.cc",
+    "//starboard/shared/iso/string_get_length.cc",
+    "//starboard/shared/iso/string_get_length_wide.cc",
+    "//starboard/shared/iso/string_parse_double.cc",
+    "//starboard/shared/iso/string_parse_signed_integer.cc",
+    "//starboard/shared/iso/string_parse_uint64.cc",
+    "//starboard/shared/iso/string_parse_unsigned_integer.cc",
+    "//starboard/shared/iso/string_scan.cc",
+    "//starboard/shared/iso/system_binary_search.cc",
+    "//starboard/shared/iso/system_sort.cc",
+    "//starboard/shared/libevent/socket_waiter_add.cc",
+    "//starboard/shared/libevent/socket_waiter_create.cc",
+    "//starboard/shared/libevent/socket_waiter_destroy.cc",
+    "//starboard/shared/libevent/socket_waiter_internal.cc",
+    "//starboard/shared/libevent/socket_waiter_remove.cc",
+    "//starboard/shared/libevent/socket_waiter_wait.cc",
+    "//starboard/shared/libevent/socket_waiter_wait_timed.cc",
+    "//starboard/shared/libevent/socket_waiter_wake_up.cc",
+    "//starboard/shared/libvpx/vpx_video_decoder.cc",
+    "//starboard/shared/libvpx/vpx_video_decoder.h",
+    "//starboard/shared/linux/byte_swap.cc",
+    "//starboard/shared/linux/get_home_directory.cc",
+    "//starboard/shared/linux/memory_get_stack_bounds.cc",
+    "//starboard/shared/linux/page_internal.cc",
+    "//starboard/shared/linux/socket_get_interface_address.cc",
+    "//starboard/shared/linux/socket_get_local_interface_address.cc",
+    "//starboard/shared/linux/system_get_random_data.cc",
+    "//starboard/shared/linux/system_get_stack.cc",
+    "//starboard/shared/linux/system_get_total_cpu_memory.cc",
+    "//starboard/shared/linux/system_is_debugger_attached.cc",
+    "//starboard/shared/linux/system_symbolize.cc",
+    "//starboard/shared/linux/thread_get_id.cc",
+    "//starboard/shared/linux/thread_get_name.cc",
+    "//starboard/shared/linux/thread_set_name.cc",
+    "//starboard/shared/nouser/user_get_current.cc",
+    "//starboard/shared/nouser/user_get_property.cc",
+    "//starboard/shared/nouser/user_get_signed_in.cc",
+    "//starboard/shared/nouser/user_internal.cc",
+    "//starboard/shared/posix/directory_create.cc",
+    "//starboard/shared/posix/file_can_open.cc",
+    "//starboard/shared/posix/file_close.cc",
+    "//starboard/shared/posix/file_delete.cc",
+    "//starboard/shared/posix/file_exists.cc",
+    "//starboard/shared/posix/file_flush.cc",
+    "//starboard/shared/posix/file_get_info.cc",
+    "//starboard/shared/posix/file_get_path_info.cc",
+    "//starboard/shared/posix/file_open.cc",
+    "//starboard/shared/posix/file_read.cc",
+    "//starboard/shared/posix/file_seek.cc",
+    "//starboard/shared/posix/file_truncate.cc",
+    "//starboard/shared/posix/file_write.cc",
+    "//starboard/shared/posix/log.cc",
+    "//starboard/shared/posix/log_flush.cc",
+    "//starboard/shared/posix/log_format.cc",
+    "//starboard/shared/posix/log_is_tty.cc",
+    "//starboard/shared/posix/log_raw.cc",
+    "//starboard/shared/posix/memory_flush.cc",
+    "//starboard/shared/posix/set_non_blocking_internal.cc",
+    "//starboard/shared/posix/socket_accept.cc",
+    "//starboard/shared/posix/socket_bind.cc",
+    "//starboard/shared/posix/socket_clear_last_error.cc",
+    "//starboard/shared/posix/socket_connect.cc",
+    "//starboard/shared/posix/socket_create.cc",
+    "//starboard/shared/posix/socket_destroy.cc",
+    "//starboard/shared/posix/socket_free_resolution.cc",
+    "//starboard/shared/posix/socket_get_last_error.cc",
+    "//starboard/shared/posix/socket_get_local_address.cc",
+    "//starboard/shared/posix/socket_internal.cc",
+    "//starboard/shared/posix/socket_is_connected.cc",
+    "//starboard/shared/posix/socket_is_connected_and_idle.cc",
+    "//starboard/shared/posix/socket_join_multicast_group.cc",
+    "//starboard/shared/posix/socket_listen.cc",
+    "//starboard/shared/posix/socket_receive_from.cc",
+    "//starboard/shared/posix/socket_resolve.cc",
+    "//starboard/shared/posix/socket_send_to.cc",
+    "//starboard/shared/posix/socket_set_broadcast.cc",
+    "//starboard/shared/posix/socket_set_receive_buffer_size.cc",
+    "//starboard/shared/posix/socket_set_reuse_address.cc",
+    "//starboard/shared/posix/socket_set_send_buffer_size.cc",
+    "//starboard/shared/posix/socket_set_tcp_keep_alive.cc",
+    "//starboard/shared/posix/socket_set_tcp_no_delay.cc",
+    "//starboard/shared/posix/socket_set_tcp_window_scaling.cc",
+    "//starboard/shared/posix/string_compare_no_case.cc",
+    "//starboard/shared/posix/string_compare_no_case_n.cc",
+    "//starboard/shared/posix/string_compare_wide.cc",
+    "//starboard/shared/posix/string_format.cc",
+    "//starboard/shared/posix/string_format_wide.cc",
+    "//starboard/shared/posix/system_break_into_debugger.cc",
+    "//starboard/shared/posix/system_clear_last_error.cc",
+    "//starboard/shared/posix/system_get_error_string.cc",
+    "//starboard/shared/posix/system_get_last_error.cc",
+    "//starboard/shared/posix/system_get_locale_id.cc",
+    "//starboard/shared/posix/system_get_number_of_processors.cc",
+    "//starboard/shared/posix/thread_sleep.cc",
+    "//starboard/shared/posix/time_get_monotonic_now.cc",
+    "//starboard/shared/posix/time_get_monotonic_thread_now.cc",
+    "//starboard/shared/posix/time_get_now.cc",
+    "//starboard/shared/posix/time_zone_get_current.cc",
+    "//starboard/shared/posix/time_zone_get_dst_name.cc",
+    "//starboard/shared/posix/time_zone_get_name.cc",
+    "//starboard/shared/pthread/condition_variable_broadcast.cc",
+    "//starboard/shared/pthread/condition_variable_create.cc",
+    "//starboard/shared/pthread/condition_variable_destroy.cc",
+    "//starboard/shared/pthread/condition_variable_signal.cc",
+    "//starboard/shared/pthread/condition_variable_wait.cc",
+    "//starboard/shared/pthread/condition_variable_wait_timed.cc",
+    "//starboard/shared/pthread/mutex_acquire.cc",
+    "//starboard/shared/pthread/mutex_acquire_try.cc",
+    "//starboard/shared/pthread/mutex_create.cc",
+    "//starboard/shared/pthread/mutex_destroy.cc",
+    "//starboard/shared/pthread/mutex_release.cc",
+    "//starboard/shared/pthread/once.cc",
+    "//starboard/shared/pthread/thread_create.cc",
+    "//starboard/shared/pthread/thread_create_local_key.cc",
+    "//starboard/shared/pthread/thread_create_priority.h",
+    "//starboard/shared/pthread/thread_destroy_local_key.cc",
+    "//starboard/shared/pthread/thread_detach.cc",
+    "//starboard/shared/pthread/thread_get_current.cc",
+    "//starboard/shared/pthread/thread_get_local_value.cc",
+    "//starboard/shared/pthread/thread_is_equal.cc",
+    "//starboard/shared/pthread/thread_join.cc",
+    "//starboard/shared/pthread/thread_set_local_value.cc",
+    "//starboard/shared/pthread/thread_yield.cc",
+    "//starboard/shared/signal/crash_signals.h",
+    "//starboard/shared/signal/crash_signals_sigaction.cc",
+    "//starboard/shared/signal/suspend_signals.cc",
+    "//starboard/shared/signal/suspend_signals.h",
+    "//starboard/shared/starboard/application.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_create.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_destroy.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_internal.cc",
+    "//starboard/shared/starboard/audio_sink/audio_sink_internal.h",
+    "//starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc",
+    "//starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc",
+    "//starboard/shared/starboard/audio_sink/stub_audio_sink_type.h",
+    "//starboard/shared/starboard/command_line.cc",
+    "//starboard/shared/starboard/command_line.h",
+    "//starboard/shared/starboard/directory_can_open.cc",
+    "//starboard/shared/starboard/event_cancel.cc",
+    "//starboard/shared/starboard/event_schedule.cc",
+    "//starboard/shared/starboard/file_mode_string_to_flags.cc",
+    "//starboard/shared/starboard/file_storage/storage_close_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_delete_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_get_record_size.cc",
+    "//starboard/shared/starboard/file_storage/storage_open_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_read_record.cc",
+    "//starboard/shared/starboard/file_storage/storage_write_record.cc",
+    "//starboard/shared/starboard/log_message.cc",
+    "//starboard/shared/starboard/log_raw_dump_stack.cc",
+    "//starboard/shared/starboard/log_raw_format.cc",
+    "//starboard/shared/starboard/media/codec_util.cc",
+    "//starboard/shared/starboard/media/codec_util.h",
+    "//starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc",
+    "//starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc",
+    "//starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc",
+    "//starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc",
+    "//starboard/shared/starboard/media/media_is_output_protected.cc",
+    "//starboard/shared/starboard/media/media_set_output_protection.cc",
+    "//starboard/shared/starboard/media/media_util.cc",
+    "//starboard/shared/starboard/media/media_util.h",
+    "//starboard/shared/starboard/media/mime_type.cc",
+    "//starboard/shared/starboard/media/mime_type.h",
+    "//starboard/shared/starboard/new.cc",
+    "//starboard/shared/starboard/player/decoded_audio_internal.cc",
+    "//starboard/shared/starboard/player/decoded_audio_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_decoder_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_frame_tracker.h",
+    "//starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc",
+    "//starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_renderer_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_time_stretcher.cc",
+    "//starboard/shared/starboard/player/filter/audio_time_stretcher.h",
+    "//starboard/shared/starboard/player/filter/decoded_audio_queue.cc",
+    "//starboard/shared/starboard/player/filter/decoded_audio_queue.h",
+    "//starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc",
+    "//starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h",
+    "//starboard/shared/starboard/player/filter/player_components.h",
+    "//starboard/shared/starboard/player/filter/video_decoder_internal.h",
+    "//starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc",
+    "//starboard/shared/starboard/player/filter/video_renderer_impl_internal.h",
+    "//starboard/shared/starboard/player/filter/video_renderer_internal.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",
+    "//starboard/shared/starboard/player/input_buffer_internal.h",
+    "//starboard/shared/starboard/player/job_queue.cc",
+    "//starboard/shared/starboard/player/job_queue.h",
+    "//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_internal.cc",
+    "//starboard/shared/starboard/player/player_internal.h",
+    "//starboard/shared/starboard/player/player_output_mode_supported.cc",
+    "//starboard/shared/starboard/player/player_seek.cc",
+    "//starboard/shared/starboard/player/player_set_bounds.cc",
+    "//starboard/shared/starboard/player/player_set_pause.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/player/video_frame_internal.cc",
+    "//starboard/shared/starboard/player/video_frame_internal.h",
+    "//starboard/shared/starboard/queue_application.cc",
+    "//starboard/shared/starboard/string_concat.cc",
+    "//starboard/shared/starboard/string_concat_wide.cc",
+    "//starboard/shared/starboard/string_copy.cc",
+    "//starboard/shared/starboard/string_copy_wide.cc",
+    "//starboard/shared/starboard/string_duplicate.cc",
+    "//starboard/shared/starboard/system_get_random_uint64.cc",
+    "//starboard/shared/starboard/system_request_pause.cc",
+    "//starboard/shared/starboard/system_request_stop.cc",
+    "//starboard/shared/starboard/system_request_suspend.cc",
+    "//starboard/shared/starboard/system_request_unpause.cc",
+    "//starboard/shared/starboard/window_set_default_options.cc",
+    "//starboard/shared/stub/accessibility_get_display_settings.cc",
+    "//starboard/shared/stub/accessibility_get_text_to_speech_settings.cc",
+    "//starboard/shared/stub/cryptography_create_transformer.cc",
+    "//starboard/shared/stub/cryptography_destroy_transformer.cc",
+    "//starboard/shared/stub/cryptography_get_tag.cc",
+    "//starboard/shared/stub/cryptography_set_authenticated_data.cc",
+    "//starboard/shared/stub/cryptography_set_initialization_vector.cc",
+    "//starboard/shared/stub/cryptography_transform.cc",
+    "//starboard/shared/stub/drm_close_session.cc",
+    "//starboard/shared/stub/drm_create_system.cc",
+    "//starboard/shared/stub/drm_destroy_system.cc",
+    "//starboard/shared/stub/drm_generate_session_update_request.cc",
+    "//starboard/shared/stub/drm_system_internal.h",
+    "//starboard/shared/stub/drm_update_session.cc",
+    "//starboard/shared/stub/image_decode.cc",
+    "//starboard/shared/stub/image_is_decode_supported.cc",
+    "//starboard/shared/stub/media_is_supported.cc",
+    "//starboard/shared/stub/media_is_transfer_characteristics_supported.cc",
+    "//starboard/shared/stub/microphone_close.cc",
+    "//starboard/shared/stub/microphone_create.cc",
+    "//starboard/shared/stub/microphone_destroy.cc",
+    "//starboard/shared/stub/microphone_get_available.cc",
+    "//starboard/shared/stub/microphone_is_sample_rate_supported.cc",
+    "//starboard/shared/stub/microphone_open.cc",
+    "//starboard/shared/stub/microphone_read.cc",
+    "//starboard/shared/stub/system_clear_platform_error.cc",
+    "//starboard/shared/stub/system_get_total_gpu_memory.cc",
+    "//starboard/shared/stub/system_get_used_gpu_memory.cc",
+    "//starboard/shared/stub/system_hide_splash_screen.cc",
+    "//starboard/shared/stub/system_raise_platform_error.cc",
+  ]
+
+  if (use_dlmalloc_allocator) {
+    sources += [
+      "//starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc",
+      "//starboard/shared/dlmalloc/memory_allocate_unchecked.cc",
+      "//starboard/shared/dlmalloc/memory_free.cc",
+      "//starboard/shared/dlmalloc/memory_free_aligned.cc",
+      "//starboard/shared/dlmalloc/memory_reallocate_unchecked.cc",
+      "//starboard/shared/dlmalloc/system_get_used_cpu_memory.cc",
+    ]
+  } else {
+    sources += [
+      "//starboard/shared/iso/memory_allocate_unchecked.cc",
+      "//starboard/shared/iso/memory_free.cc",
+      "//starboard/shared/iso/memory_reallocate_unchecked.cc",
+      "//starboard/shared/linux/system_get_used_cpu_memory.cc",
+      "//starboard/shared/posix/memory_allocate_aligned_unchecked.cc",
+      "//starboard/shared/posix/memory_free_aligned.cc",
+    ]
+  }
+
+  defines = [
+    # This must be defined when building Starboard, and must not when
+    # building Starboard client code.
+    "STARBOARD_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//starboard/common",
+    ":starboard_base_symbolize",
+    "//third_party/dlmalloc",
+    "//third_party/libevent",
+    "//third_party/libvpx",
+  ]
+}
+
+source_set("starboard_base_symbolize") {
+  sources = [
+    "//base/third_party/symbolize/demangle.cc",
+    "//base/third_party/symbolize/symbolize.cc",
+  ]
+}
diff --git a/src/starboard/linux/shared/buildconfig.gni b/src/starboard/linux/shared/buildconfig.gni
new file mode 100644
index 0000000..9cbc874
--- /dev/null
+++ b/src/starboard/linux/shared/buildconfig.gni
@@ -0,0 +1,43 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/linux/shared/clang.gni")
+
+###############################################################################
+# This file is imported into BUILDCONFIG.gn. As a side effect, any variable
+# defined here becomes a global variable in all GN files.
+#
+# Adding new variables to this file could result in variable name conflicts, so
+# keep the number of variables in this file to a minimum!
+#
+# If you need to define a temporary variable, prefix it with an underscore like
+# _this. Prefixing a variable with an underscore makes it a private variable
+# that won't bleed into other files.
+###############################################################################
+
+# Target OS and CPU.
+# BUILDCONFIG.gn sets target_os and target_cpu to the values of these two
+# variables below. Unfortunately, due to GN's restrictions on how variables can
+# be changed, we can't directly set target_os and target_cpu here.
+target_os_ = "linux"
+target_cpu_ = "x64"
+
+# The target and host toolchain
+if (use_clang) {
+  target_toolchain = "//starboard/build/toolchain/linux:clang_x64"
+  host_toolchain = "//starboard/build/toolchain/linux:clang_$host_cpu"
+} else {
+  target_toolchain = "//starboard/build/toolchain/linux:x64"
+  host_toolchain = "//starboard/build/toolchain/linux:$host_cpu"
+}
diff --git a/src/starboard/linux/shared/clang.gni b/src/starboard/linux/shared/clang.gni
new file mode 100644
index 0000000..a9aa3f4
--- /dev/null
+++ b/src/starboard/linux/shared/clang.gni
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Whether to use Clang.
+  use_clang = true
+}
diff --git a/src/starboard/linux/shared/configuration.gni b/src/starboard/linux/shared/configuration.gni
new file mode 100644
index 0000000..99cb78e
--- /dev/null
+++ b/src/starboard/linux/shared/configuration.gni
@@ -0,0 +1,46 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set to true to build with DIAL support.
+enable_in_app_dial = true
+
+# 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
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "system_gles2" - Use the system implementation of EGL + GLES2. The
+#                    headers and libraries must be on the system include and
+#                    link paths.
+#   "glimp"  - Cobalt's own EGL + GLES2 implementation. This requires a
+#              valid Glimp implementation for the platform.
+#   "angle"  - A DirectX-to-OpenGL adaptation layer. This requires a valid
+#              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"
+
+# Use media source extension implementation that is conformed to the
+# Candidate Recommandation of July 5th 2016.
+cobalt_use_media_source_2016 = true
+
+# Use ASAN by default when building with Clang.
+use_asan_by_default = true
+
+declare_args() {
+  # Set to true to enable distributed compilation using Goma. By default we
+  # use Goma for stub and linux.
+  use_goma = true
+}
diff --git a/src/starboard/linux/shared/dlmalloc.gni b/src/starboard/linux/shared/dlmalloc.gni
new file mode 100644
index 0000000..73c1137
--- /dev/null
+++ b/src/starboard/linux/shared/dlmalloc.gni
@@ -0,0 +1,19 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Whether to use the dlmalloc allocator instead of the system allocator
+  use_dlmalloc_allocator = false
+}
+
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py
index 73e708a..3b476d1 100644
--- a/src/starboard/linux/shared/launcher.py
+++ b/src/starboard/linux/shared/launcher.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
 #
 # Copyright 2017 Google Inc. All Rights Reserved.
 #
@@ -15,17 +14,25 @@
 # limitations under the License.
 """Linux implementation of Starboard launcher abstraction."""
 
-import imp
+import importlib
 import os
+import sys
+
+if "environment" in sys.modules:
+  environment = sys.modules["environment"]
+else:
+  env_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir,
+                                          os.pardir, "tools"))
+  if env_path not in sys.path:
+    sys.path.append(env_path)
+  environment = importlib.import_module("environment")
+
+
 import signal
 import socket
 import subprocess
 
-module_path = os.path.abspath(
-    os.path.join(os.path.dirname(__file__),
-                 os.pardir, os.pardir, "tools", "abstract_launcher.py"))
-
-abstract_launcher = imp.load_source("abstract_launcher", module_path)
+import starboard.tools.abstract_launcher as abstract_launcher
 
 
 class Launcher(abstract_launcher.AbstractLauncher):
@@ -57,6 +64,7 @@
     proc = subprocess.Popen([self.executable] + self.target_command_line_params)
     self.pid = proc.pid
     proc.wait()
+    return proc.returncode
 
   def Kill(self):
     print "\n***Killing Launcher***\n"
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index f896d17..311618c 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -90,6 +90,8 @@
       '<(DEPTH)/starboard/shared/libvpx/vpx_video_decoder.cc',
       '<(DEPTH)/starboard/shared/libvpx/vpx_video_decoder.h',
       '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
+      '<(DEPTH)/starboard/shared/linux/dev_input/dev_input.cc',
+      '<(DEPTH)/starboard/shared/linux/dev_input/dev_input.h',
       '<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
       '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
       '<(DEPTH)/starboard/shared/linux/page_internal.cc',
@@ -98,7 +100,6 @@
       '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
       '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
       '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
-      '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
       '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
       '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
       '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
@@ -326,12 +327,14 @@
           '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
           '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
           '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
+          '<(DEPTH)/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc',
         ],
       }, {
         'starboard_platform_sources': [
           '<(DEPTH)/starboard/shared/iso/memory_allocate_unchecked.cc',
           '<(DEPTH)/starboard/shared/iso/memory_free.cc',
           '<(DEPTH)/starboard/shared/iso/memory_reallocate_unchecked.cc',
+          '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
           '<(DEPTH)/starboard/shared/posix/memory_allocate_aligned_unchecked.cc',
           '<(DEPTH)/starboard/shared/posix/memory_free_aligned.cc',
         ],
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index 4ae443b..315903be 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -36,14 +36,12 @@
                          home_path, kMaxPathSize)) {
     return false;
   }
-  int result =
-      SbStringFormatF(out_path, path_size, "%s/.cache/cobalt", home_path);
+  int result = SbStringFormatF(out_path, path_size, "%s/.cache", home_path);
   if (result < 0 || result >= path_size) {
     out_path[0] = '\0';
     return false;
   }
-
-  return true;
+  return SbDirectoryCreate(out_path);
 }
 
 // Places up to |path_size| - 1 characters of the path to the current
@@ -144,7 +142,12 @@
       if (!GetCacheDirectory(path, kPathSize)) {
         return false;
       }
-      SbDirectoryCreate(path);
+      if (SbStringConcat(path, "/cobalt", kPathSize) >= kPathSize) {
+        return false;
+      }
+      if (!SbDirectoryCreate(path)) {
+        return false;
+      }
       break;
     case kSbSystemPathDebugOutputDirectory:
       if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) {
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.py b/src/starboard/linux/x64directfb/future/gyp_configuration.py
index b31713a..1074bc3 100644
--- a/src/starboard/linux/x64directfb/future/gyp_configuration.py
+++ b/src/starboard/linux/x64directfb/future/gyp_configuration.py
@@ -14,15 +14,9 @@
 """Starboard Linux X64 DirectFB future platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64directfb/gyp_configuration.py b/src/starboard/linux/x64directfb/gyp_configuration.py
index 9933052..7e60294 100644
--- a/src/starboard/linux/x64directfb/gyp_configuration.py
+++ b/src/starboard/linux/x64directfb/gyp_configuration.py
@@ -14,13 +14,9 @@
 """Starboard Linux X64 DirectFB platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(os.path.realpath(os.path.join(
-    os.path.dirname(__file__), os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64x11/BUILD.gn b/src/starboard/linux/x64x11/BUILD.gn
new file mode 100644
index 0000000..259502b
--- /dev/null
+++ b/src/starboard/linux/x64x11/BUILD.gn
@@ -0,0 +1,98 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/delegated_config.gni")
+
+# =============================================================================
+# DEFAULT COMPILER CONFIGS
+# =============================================================================
+
+config("compiler_defaults") {
+  libs = [
+    "EGL",
+    "GLESv2",
+    "X11",
+    "Xcomposite",
+    "Xrender",
+  ]
+
+  configs = [ "//starboard/linux/shared:compiler_defaults" ]
+}
+
+config("compiler_defaults_debug") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_debug" ]
+}
+
+config("compiler_defaults_devel") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_devel" ]
+}
+
+config("compiler_defaults_qa") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_qa" ]
+}
+
+config("compiler_defaults_gold") {
+  configs = [ "//starboard/linux/shared:compiler_defaults_gold" ]
+}
+
+# =============================================================================
+# DELEGATED CONFIGS
+# =============================================================================
+
+delegated_config("pedantic_warnings") {
+  path = "//starboard/linux/shared"
+  generate_default = false
+}
+
+delegated_config("optimizations") {
+  path = "//starboard/linux/shared"
+  prefixes = [ "no", "debuggable", "full" ]
+}
+
+delegated_config("rtti") {
+  path = "//starboard/linux/shared"
+}
+
+config("wexit_time_destructors") {
+  configs = [ "//starboard/linux/shared:wexit_time_destructors" ]
+}
+
+# =============================================================================
+# starboard_platform TARGET
+# =============================================================================
+
+static_library("starboard_platform") {
+  sources = [
+    "//starboard/linux/x64x11/main.cc",
+    "//starboard/linux/x64x11/sanitizer_options.cc",
+    "//starboard/linux/x64x11/system_get_property.cc",
+    "//starboard/shared/starboard/link_receiver.cc",
+    "//starboard/shared/x11/application_x11.cc",
+    "//starboard/shared/x11/window_create.cc",
+    "//starboard/shared/x11/window_destroy.cc",
+    "//starboard/shared/x11/window_get_platform_handle.cc",
+    "//starboard/shared/x11/window_get_size.cc",
+    "//starboard/shared/x11/window_internal.cc",
+  ]
+  
+  defines = [
+    # This must be defined when building Starboard, and must not when
+    # building Starboard client code.
+    "STARBOARD_IMPLEMENTATION",
+  ]
+
+  deps = [
+    "//starboard/linux/shared:starboard_platform",
+  ]
+}
diff --git a/src/starboard/linux/x64x11/buildconfig.gni b/src/starboard/linux/x64x11/buildconfig.gni
new file mode 100644
index 0000000..4335652
--- /dev/null
+++ b/src/starboard/linux/x64x11/buildconfig.gni
@@ -0,0 +1,15 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/linux/shared/buildconfig.gni")
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 67a704c..58d9f75 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -14,20 +14,12 @@
 """Starboard Linux X64 X11 Clang 3.6 platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
-            'shared')))
-# pylint: disable=import-self,g-import-not-at-top
-import gyp_configuration as shared_configuration
+from starboard.linux.shared import gyp_configuration
 
 
-class PlatformConfig(shared_configuration.PlatformConfig):
+class PlatformConfig(gyp_configuration.PlatformConfig):
   """Starboard Linux platform configuration."""
 
   def __init__(self, platform, asan_enabled_by_default=True):
diff --git a/src/starboard/linux/x64x11/configuration.gni b/src/starboard/linux/x64x11/configuration.gni
new file mode 100644
index 0000000..bb3a238
--- /dev/null
+++ b/src/starboard/linux/x64x11/configuration.gni
@@ -0,0 +1,19 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/linux/shared/configuration.gni")
+
+# Enable support for the map to mesh filter, which is primarily used to
+# implement spherical video playback.
+enable_map_to_mesh = true
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.py b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
index 4fc899f..ca2ca64 100644
--- a/src/starboard/linux/x64x11/directgles/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
@@ -14,16 +14,9 @@
 """Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
-# pylint: disable=import-self,g-import-not-at-top
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.py b/src/starboard/linux/x64x11/future/gyp_configuration.py
index 5f82ad7..3a3e34f 100644
--- a/src/starboard/linux/x64x11/future/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.py
@@ -14,15 +14,9 @@
 """Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
 
 import logging
-import os
-import sys
 
 # Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
 
 
 def CreatePlatformConfig():
diff --git a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
index bac3a98..3c1702d 100644
--- a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
@@ -16,20 +16,15 @@
 import logging
 import os
 import subprocess
-import sys
 
-# Import the shared Linux platform configuration.
-sys.path.append(
-    os.path.realpath(
-        os.path.join(
-            os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
-            'shared')))
+
 # pylint: disable=import-self,g-import-not-at-top
-import gyp_configuration as shared_configuration
 import gyp_utils
+# Import the shared Linux platform configuration.
+from starboard.linux.shared import gyp_configuration
 
 
-class PlatformConfig(shared_configuration.PlatformConfig):
+class PlatformConfig(gyp_configuration.PlatformConfig):
   """Starboard Linux platform configuration."""
 
   def __init__(self, platform, asan_enabled_by_default=False):
diff --git a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
index 659c47d..f76e9ea 100644
--- a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
@@ -29,6 +29,7 @@
     'cobalt_enable_jit': 0,
 
     'cobalt_media_source_2016': 1,
+    'cobalt_encrypted_media_extension_enable_key_statuses_update': 0,
 
     'platform_libraries': [
       '-lpthread',
diff --git a/src/starboard/log.h b/src/starboard/log.h
index 596bc88..bd0edef 100644
--- a/src/starboard/log.h
+++ b/src/starboard/log.h
@@ -145,7 +145,7 @@
 }
 
 #if defined(__cplusplus_winrt)
-inline std::ostream& operator<<(std::ostream& out, ::Platform::String ^ str) {
+inline std::ostream& operator<<(std::ostream& out, ::Platform::String^ str) {
   return out << std::wstring(str->Begin(), str->End());
 }
 #endif
@@ -156,7 +156,10 @@
 const SbLogPriority SB_LOG_FATAL = kSbLogPriorityFatal;
 const SbLogPriority SB_LOG_0 = SB_LOG_ERROR;
 
-class SB_EXPORT LogMessage {
+// TODO: Export this, e.g. by wrapping in straight-C functions which can then be
+// SB_EXPORT'd. Using SB_EXPORT here directly causes issues on Windows because
+// it uses std classes which also need to be exported.
+class LogMessage {
  public:
   LogMessage(const char* file, int line, SbLogPriority priority);
   ~LogMessage();
diff --git a/src/starboard/nplb/BUILD.gn b/src/starboard/nplb/BUILD.gn
new file mode 100644
index 0000000..29d0b35
--- /dev/null
+++ b/src/starboard/nplb/BUILD.gn
@@ -0,0 +1,297 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/deploy.gni")
+
+group("all") {
+  testonly = true
+  deps = [
+    ":nplb",
+    ":deploy",
+  ]
+}
+
+test("nplb") {
+  testonly = true
+  sources = [
+    "//starboard/common/test_main.cc",
+    "accessibility_get_setting_test.cc",
+    "align_test.cc",
+    "atomic_test.cc",
+    "audio_sink_create_test.cc",
+    "audio_sink_destroy_test.cc",
+    "audio_sink_get_max_channels_test.cc",
+    "audio_sink_get_nearest_supported_sample_frequency_test.cc",
+    "audio_sink_helpers.cc",
+    "audio_sink_helpers.h",
+    "audio_sink_is_audio_frame_storage_type_supported_test.cc",
+    "audio_sink_is_audio_sample_type_supported_test.cc",
+    "audio_sink_test.cc",
+    "blitter_blit_rect_to_rect_test.cc",
+    "blitter_blit_rect_to_rect_tiled_test.cc",
+    "blitter_blit_rects_to_rects_test.cc",
+    "blitter_create_context_test.cc",
+    "blitter_create_default_device_test.cc",
+    "blitter_create_pixel_data_test.cc",
+    "blitter_create_render_target_surface_test.cc",
+    "blitter_create_surface_from_pixel_data_test.cc",
+    "blitter_create_swap_chain_from_window_test.cc",
+    "blitter_destroy_context_test.cc",
+    "blitter_destroy_device_test.cc",
+    "blitter_destroy_pixel_data_test.cc",
+    "blitter_destroy_surface_test.cc",
+    "blitter_destroy_swap_chain_test.cc",
+    "blitter_download_surface_pixels_test.cc",
+    "blitter_fill_rect_test.cc",
+    "blitter_flip_swap_chain_test.cc",
+    "blitter_flush_context_test.cc",
+    "blitter_get_max_contexts_test.cc",
+    "blitter_get_pixel_data_pitch_in_bytes_test.cc",
+    "blitter_get_pixel_data_pointer_test.cc",
+    "blitter_get_render_target_from_surface_test.cc",
+    "blitter_get_render_target_from_swap_chain_test.cc",
+    "blitter_get_surface_info_test.cc",
+    "blitter_helpers.cc",
+    "blitter_is_pixel_format_supported_by_download_surface_pixels_test.cc",
+    "blitter_is_pixel_format_supported_by_pixel_data_test.cc",
+    "blitter_is_pixel_format_supported_by_render_target_surface_test.cc",
+    "blitter_set_blending_test.cc",
+    "blitter_set_modulate_blits_with_color_test.cc",
+    "blitter_set_render_target_test.cc",
+    "blitter_set_scissor_test.cc",
+    "byte_swap_test.cc",
+    "char_is_signed_test.cc",
+    "character_is_alphanumeric_test.cc",
+    "character_is_digit_test.cc",
+    "character_is_hex_digit_test.cc",
+    "character_is_space_test.cc",
+    "character_is_upper_test.cc",
+    "character_to_lower_test.cc",
+    "character_to_upper_test.cc",
+    "condition_variable_broadcast_test.cc",
+    "condition_variable_create_test.cc",
+    "condition_variable_destroy_test.cc",
+    "condition_variable_signal_test.cc",
+    "condition_variable_wait_test.cc",
+    "condition_variable_wait_timed_test.cc",
+    "configuration_test.cc",
+    "cryptography_create_transformer_test.cc",
+    "cryptography_helpers.cc",
+    "cryptography_helpers.h",
+    "cryptography_transform_gcm_test.cc",
+    "cryptography_transform_test.cc",
+    "decode_target_create_test.cc",
+    "decode_target_provider_test.cc",
+    "directory_can_open_test.cc",
+    "directory_close_test.cc",
+    "directory_create_test.cc",
+    "directory_get_next_test.cc",
+    "directory_open_test.cc",
+    "double_absolute_test.cc",
+    "double_exponent_test.cc",
+    "double_floor_test.cc",
+    "double_is_finite_test.cc",
+    "double_is_nan_test.cc",
+    "file_can_open_test.cc",
+    "file_close_test.cc",
+    "file_get_info_test.cc",
+    "file_get_path_info_test.cc",
+    "file_helpers.cc",
+    "file_mode_string_to_flags_test.cc",
+    "file_open_test.cc",
+    "file_read_test.cc",
+    "file_seek_test.cc",
+    "file_truncate_test.cc",
+    "file_write_test.cc",
+    "flat_map_test.cc",
+    "include_all.c",
+    "include_all_too.c",
+    "log_flush_test.cc",
+    "log_format_test.cc",
+    "log_is_tty_test.cc",
+    "log_raw_dump_stack_test.cc",
+    "log_raw_test.cc",
+    "log_test.cc",
+    "memory_align_to_page_size_test.cc",
+    "memory_allocate_aligned_test.cc",
+    "memory_allocate_test.cc",
+    "memory_compare_test.cc",
+    "memory_copy_test.cc",
+    "memory_deallocate_aligned_test.cc",
+    "memory_deallocate_test.cc",
+    "memory_find_byte_test.cc",
+    "memory_get_stack_bounds_test.cc",
+    "memory_is_zero_test.cc",
+    "memory_map_test.cc",
+    "memory_move_test.cc",
+    "memory_reallocate_test.cc",
+    "memory_reporter_test.cc",
+    "memory_set_test.cc",
+    "microphone_close_test.cc",
+    "microphone_create_test.cc",
+    "microphone_destroy_test.cc",
+    "microphone_get_available_test.cc",
+    "microphone_is_sample_rate_supported_test.cc",
+    "microphone_open_test.cc",
+    "microphone_read_test.cc",
+    "mutex_acquire_test.cc",
+    "mutex_acquire_try_test.cc",
+    "mutex_create_test.cc",
+    "mutex_destroy_test.cc",
+    "once_test.cc",
+    "optional_test.cc",
+    "player_create_test.cc",
+    "random_helpers.cc",
+    "rwlock_test.cc",
+    "semaphore_test.cc",
+    "socket_accept_test.cc",
+    "socket_bind_test.cc",
+    "socket_clear_last_error_test.cc",
+    "socket_connect_test.cc",
+    "socket_create_test.cc",
+    "socket_destroy_test.cc",
+    "socket_get_interface_address_test.cc",
+    "socket_get_last_error_test.cc",
+    "socket_get_local_address_test.cc",
+    "socket_get_local_interface_address_test.cc",
+    "socket_helpers.cc",
+    "socket_is_connected_and_idle_test.cc",
+    "socket_is_connected_test.cc",
+    "socket_join_multicast_group_test.cc",
+    "socket_listen_test.cc",
+    "socket_receive_from_test.cc",
+    "socket_resolve_test.cc",
+    "socket_send_to_test.cc",
+    "socket_set_options_test.cc",
+    "socket_waiter_add_test.cc",
+    "socket_waiter_create_test.cc",
+    "socket_waiter_destroy_test.cc",
+    "socket_waiter_remove_test.cc",
+    "socket_waiter_wait_test.cc",
+    "socket_waiter_wait_timed_test.cc",
+    "socket_waiter_wake_up_test.cc",
+    "socket_wrapper_test.cc",
+    "speech_recognizer_cancel_test.cc",
+    "speech_recognizer_create_test.cc",
+    "speech_recognizer_destroy_test.cc",
+    "speech_recognizer_helper.h",
+    "speech_recognizer_start_test.cc",
+    "speech_recognizer_stop_test.cc",
+    "speech_synthesis_basic_test.cc",
+    "storage_close_record_test.cc",
+    "storage_delete_record_test.cc",
+    "storage_get_record_size_test.cc",
+    "storage_open_record_test.cc",
+    "storage_read_record_test.cc",
+    "storage_write_record_test.cc",
+    "state_machine_test.cc",
+    "string_compare_all_test.cc",
+    "string_compare_no_case_n_test.cc",
+    "string_compare_no_case_test.cc",
+    "string_compare_test.cc",
+    "string_compare_wide_test.cc",
+    "string_concat_test.cc",
+    "string_concat_wide_test.cc",
+    "string_copy_test.cc",
+    "string_copy_wide_test.cc",
+    "string_duplicate_test.cc",
+    "string_find_character_test.cc",
+    "string_find_last_character_test.cc",
+    "string_find_string_test.cc",
+    "string_format_test.cc",
+    "string_format_wide_test.cc",
+    "string_parse_double_test.cc",
+    "string_parse_signed_integer_test.cc",
+    "string_parse_uint64_test.cc",
+    "string_parse_unsigned_integer_test.cc",
+    "string_scan_test.cc",
+    "system_binary_search_test.cc",
+    "system_clear_last_error_test.cc",
+    "system_get_error_string_test.cc",
+    "system_get_last_error_test.cc",
+    "system_get_locale_id_test.cc",
+    "system_get_number_of_processors_test.cc",
+    "system_get_path_test.cc",
+    "system_get_property_test.cc",
+    "system_get_random_data_test.cc",
+    "system_get_random_uint64_test.cc",
+    "system_get_stack_test.cc",
+    "system_get_total_cpu_memory_test.cc",
+    "system_get_total_gpu_memory_test.cc",
+    "system_get_used_cpu_memory_test.cc",
+    "system_get_used_gpu_memory_test.cc",
+    "system_has_capability_test.cc",
+    "system_hide_splash_screen_test.cc",
+    "system_is_debugger_attached_test.cc",
+    "system_sort_test.cc",
+    "system_symbolize_test.cc",
+    "thread_create_test.cc",
+    "thread_detach_test.cc",
+    "thread_get_current_test.cc",
+    "thread_get_id_test.cc",
+    "thread_get_name_test.cc",
+    "thread_helpers.cc",
+    "thread_is_equal_test.cc",
+    "thread_join_test.cc",
+    "thread_local_value_test.cc",
+    "thread_set_name_test.cc",
+    "thread_sleep_test.cc",
+    "thread_yield_test.cc",
+    "time_get_monotonic_now_test.cc",
+    "time_get_now_test.cc",
+    "time_narrow_test.cc",
+    "time_zone_get_current_test.cc",
+    "time_zone_get_dst_name_test.cc",
+    "time_zone_get_name_test.cc",
+    "user_get_current_test.cc",
+    "user_get_property_test.cc",
+    "user_get_signed_in_test.cc",
+    "window_create_test.cc",
+    "window_destroy_test.cc",
+    "window_get_platform_handle_test.cc",
+    "window_get_size_test.cc",
+  ]
+  # Include private C headers, if present.
+  private_sources = exec_script("//starboard/tools/find_private_files.py",
+                                [
+                                  rebase_path("//", root_build_dir),
+                                  "nplb/include_all_private.c",
+                                ],
+                                "list lines",
+                                [ "." ])
+  # Include private tests, if present.
+  private_sources += exec_script("//starboard/tools/find_private_files.py",
+                                 [
+                                   rebase_path("//", root_build_dir),
+                                   "nplb/*_test.cc",
+                                 ],
+                                 "list lines",
+                                 [ "." ])
+  sources += rebase_path(private_sources, ".", root_build_dir)
+
+  deps = [
+    "//starboard",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+deploy("deploy") {
+  testonly = true
+  executable_name = "nplb"
+
+  deps = [
+    ":nplb",
+  ]
+}
diff --git a/src/starboard/nplb/align_test.cc b/src/starboard/nplb/align_test.cc
new file mode 100644
index 0000000..ce53310
--- /dev/null
+++ b/src/starboard/nplb/align_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/configuration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+bool IsAligned(void *pointer, size_t alignment) {
+  return (reinterpret_cast<uintptr_t>(pointer) % alignment) == 0;
+}
+
+size_t GetAlignment(void *pointer) {
+  for (size_t alignment = static_cast<size_t>(1) << ((sizeof(size_t) * 8) - 1);
+       alignment != 0; alignment /= 2) {
+    if (IsAligned(pointer, alignment)) {
+      return alignment;
+    }
+  }
+
+  return 1;
+}
+
+struct AlignedFields {
+  char unaligned1;
+  SB_ALIGNAS(2) char by_2;
+  char unaligned2;
+  SB_ALIGNAS(4) char by_4;
+  char unaligned3;
+  SB_ALIGNAS(8) char by_8;
+  char unaligned4;
+  SB_ALIGNAS(16) char by_16;
+  char unaligned5;
+  SB_ALIGNAS(32) char by_32;
+  char unaligned6;
+  SB_ALIGNAS(64) char by_64;
+  char unaligned7;
+  SB_ALIGNAS(128) char by_128;
+  char unaligned8;
+  SB_ALIGNAS(256) char by_256;
+  char unaligned9;
+};
+
+struct AlignedFieldsOf {
+  char unaligned1;
+  SB_ALIGNAS(SB_ALIGNOF(void*)) char by_void_star;
+  char unaligned2;
+  SB_ALIGNAS(SB_ALIGNOF(int)) char by_int;
+  char unaligned3;
+  SB_ALIGNAS(SB_ALIGNOF(uint64_t)) char by_uint64_t;
+  char unaligned4;
+};
+
+TEST(SbAlignTest, AlignAsStructFieldOnStack) {
+  char unaligned = 0;
+  EXPECT_NE(1, unaligned);
+  AlignedFields aligned;
+  EXPECT_LE(2, GetAlignment(&(aligned.by_2)));
+  EXPECT_LE(4, GetAlignment(&(aligned.by_4)));
+  EXPECT_LE(8, GetAlignment(&(aligned.by_8)));
+  EXPECT_LE(16, GetAlignment(&(aligned.by_16)));
+  EXPECT_LE(32, GetAlignment(&(aligned.by_32)));
+  EXPECT_LE(64, GetAlignment(&(aligned.by_64)));
+  EXPECT_LE(128, GetAlignment(&(aligned.by_128)));
+  EXPECT_LE(256, GetAlignment(&(aligned.by_256)));
+}
+
+TEST(SbAlignTest, AlignAsStackVariable) {
+  char unaligned1 = 1;
+  SB_ALIGNAS(2) char by_2;
+  char unaligned2 = 2;
+  EXPECT_NE(unaligned2, unaligned1);  // These are to try to keep the stack
+                                      // variables around.
+  SB_ALIGNAS(4) char by_4;
+  char unaligned3 = 3;
+  EXPECT_NE(unaligned3, unaligned1);
+  SB_ALIGNAS(8) char by_8;
+  char unaligned4 = 4;
+  EXPECT_NE(unaligned4, unaligned1);
+  SB_ALIGNAS(16) char by_16;
+  char unaligned5 = 5;
+  EXPECT_NE(unaligned5, unaligned1);
+
+  EXPECT_LE(2, GetAlignment(&by_2));
+  EXPECT_LE(4, GetAlignment(&by_4));
+  EXPECT_LE(8, GetAlignment(&by_8));
+  EXPECT_LE(16, GetAlignment(&by_16));
+
+#if !SB_HAS_QUIRK(DOES_NOT_STACK_ALIGN_OVER_16_BYTES)
+  SB_ALIGNAS(32) char by_32;
+  char unaligned6 = 6;
+  EXPECT_NE(unaligned6, unaligned1);
+  SB_ALIGNAS(64) char by_64;
+  char unaligned7 = 7;
+  EXPECT_NE(unaligned7, unaligned1);
+  SB_ALIGNAS(128) char by_128;
+  char unaligned8 = 8;
+  EXPECT_NE(unaligned8, unaligned1);
+  SB_ALIGNAS(256) char by_256;
+  char unaligned9 = 9;
+  EXPECT_NE(unaligned9, unaligned1);
+
+  EXPECT_LE(32, GetAlignment(&by_32));
+  EXPECT_LE(64, GetAlignment(&by_64));
+  EXPECT_LE(128, GetAlignment(&by_128));
+  EXPECT_LE(256, GetAlignment(&by_256));
+#endif  // !SB_HAS_QUIRK(DOES_NOT_STACK_ALIGN_OVER_16_BYTES)
+}
+
+TEST(SbAlignTest, AlignOf) {
+  EXPECT_LE(sizeof(uint8_t), SB_ALIGNOF(uint8_t));
+  EXPECT_LE(sizeof(uint16_t), SB_ALIGNOF(uint16_t));
+  EXPECT_LE(sizeof(uint32_t), SB_ALIGNOF(uint32_t));
+  EXPECT_LE(sizeof(uint64_t), SB_ALIGNOF(uint64_t));
+  EXPECT_LE(sizeof(uintptr_t), SB_ALIGNOF(uintptr_t));
+  EXPECT_LE(sizeof(void*), SB_ALIGNOF(void*));
+}
+
+TEST(SbAlignTest, AlignAsAlignOfStructFieldOnStack) {
+  char unaligned = 0;
+  EXPECT_NE(1, unaligned);
+
+  AlignedFieldsOf aligned;
+  EXPECT_LE(SB_ALIGNOF(void*), GetAlignment(&(aligned.by_void_star)));
+  EXPECT_LE(SB_ALIGNOF(int), GetAlignment(&(aligned.by_int)));
+  EXPECT_LE(SB_ALIGNOF(uint64_t), GetAlignment(&(aligned.by_uint64_t)));
+}
+
+TEST(SbAlignTest, AlignAsAlignOfStackVariable) {
+  char unaligned = 0;
+  EXPECT_NE(1, unaligned);
+  SB_ALIGNAS(SB_ALIGNOF(void*)) char by_void_star;
+  SB_ALIGNAS(SB_ALIGNOF(int)) char by_int;
+  SB_ALIGNAS(SB_ALIGNOF(uint64_t)) char by_uint64_t;
+
+  EXPECT_LE(SB_ALIGNOF(void*), GetAlignment(&by_void_star));
+  EXPECT_LE(SB_ALIGNOF(int), GetAlignment(&by_int));
+  EXPECT_LE(SB_ALIGNOF(uint64_t), GetAlignment(&by_uint64_t));
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/file_helpers.cc b/src/starboard/nplb/file_helpers.cc
index f6a9f43..07421a9 100644
--- a/src/starboard/nplb/file_helpers.cc
+++ b/src/starboard/nplb/file_helpers.cc
@@ -41,6 +41,13 @@
 }
 
 // static
+std::string ScopedRandomFile::MakeRandomFilename() {
+  std::ostringstream filename_stream;
+  filename_stream << "ScopedRandomFile.File_" << SbSystemGetRandomUInt64();
+  return filename_stream.str();
+}
+
+// static
 void ScopedRandomFile::ExpectPattern(int pattern_offset,
                                      void* buffer,
                                      int size,
@@ -53,22 +60,19 @@
 }
 
 // static
-std::string ScopedRandomFile::MakeRandomFilename() {
-  char path[kPathSize] = {0};
-  bool result = SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize);
-  EXPECT_TRUE(result);
-  if (!result) {
+std::string ScopedRandomFile::MakeRandomFilePath() {
+  std::ostringstream filename_stream;
+  filename_stream << GetTempDir();
+  if (!filename_stream.tellp()) {
     return "";
   }
 
-  std::ostringstream filename_stream;
-  filename_stream << path << SB_FILE_SEP_CHAR << "ScopedRandomFile.File_"
-                  << SbSystemGetRandomUInt64();
+  filename_stream << SB_FILE_SEP_CHAR << MakeRandomFilename();
   return filename_stream.str();
 }
 
 std::string ScopedRandomFile::MakeRandomFile(int length) {
-  std::string filename = MakeRandomFilename();
+  std::string filename = MakeRandomFilePath();
   if (filename.empty()) {
     return filename;
   }
diff --git a/src/starboard/nplb/file_helpers.h b/src/starboard/nplb/file_helpers.h
index ad0c946..0a8ba5b 100644
--- a/src/starboard/nplb/file_helpers.h
+++ b/src/starboard/nplb/file_helpers.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_NBLP_FILE_HELPERS_H_
-#define STARBOARD_NBLP_FILE_HELPERS_H_
+#ifndef STARBOARD_NPLB_FILE_HELPERS_H_
+#define STARBOARD_NPLB_FILE_HELPERS_H_
 
 #include <string>
 
@@ -44,7 +44,7 @@
   }
 
   // Will create a file |length| bytes long.
-  ScopedRandomFile(int length) : size_(length) {
+  explicit ScopedRandomFile(int length) : size_(length) {
     filename_ = MakeRandomFile(size_);
   }
 
@@ -52,18 +52,22 @@
   // filename.  |create| is whether to create the file or not.
   ScopedRandomFile(int length, Create create) : size_(length) {
     filename_ =
-        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilename());
+        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilePath());
   }
 
   // Will either create a file of |kDefaultLength| bytes long, or will just
   // generate a filename.  |create| is whether to create the file or not.
-  ScopedRandomFile(Create create) : size_(kDefaultLength) {
+  explicit ScopedRandomFile(Create create) : size_(kDefaultLength) {
     filename_ =
-        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilename());
+        (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilePath());
   }
 
   ~ScopedRandomFile() { SbFileDelete(filename_.c_str()); }
 
+  // Creates and returns a random filename (no path), but does not create the
+  // file.
+  static std::string MakeRandomFilename();
+
   // Returns the filename generated for this file.
   const std::string& filename() const { return filename_; }
 
@@ -83,8 +87,8 @@
   // the new file.
   static std::string MakeRandomFile(int length);
 
-  // Creates and returns a random filename, but does not create the file.
-  static std::string MakeRandomFilename();
+  // Creates and returns a path to a random file, but does not create the file.
+  static std::string MakeRandomFilePath();
 
   std::string filename_;
   int size_;
@@ -93,4 +97,4 @@
 }  // namespace nplb
 }  // namespace starboard
 
-#endif
+#endif  // STARBOARD_NPLB_FILE_HELPERS_H_
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 6c0c76f..274a67f 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -24,6 +24,7 @@
       'sources': [
         '<(DEPTH)/starboard/common/test_main.cc',
         'accessibility_get_setting_test.cc',
+        'align_test.cc',
         'atomic_test.cc',
         'audio_sink_create_test.cc',
         'audio_sink_destroy_test.cc',
@@ -146,6 +147,7 @@
         'mutex_create_test.cc',
         'mutex_destroy_test.cc',
         'once_test.cc',
+        'optional_test.cc',
         'player_create_test.cc',
         'random_helpers.cc',
         'rwlock_test.cc',
@@ -184,6 +186,7 @@
         'speech_recognizer_start_test.cc',
         'speech_recognizer_stop_test.cc',
         'speech_synthesis_basic_test.cc',
+        'state_machine_test.cc',
         'storage_close_record_test.cc',
         'storage_delete_record_test.cc',
         'storage_get_record_size_test.cc',
diff --git a/src/starboard/nplb/optional_test.cc b/src/starboard/nplb/optional_test.cc
new file mode 100644
index 0000000..c467d2a
--- /dev/null
+++ b/src/starboard/nplb/optional_test.cc
@@ -0,0 +1,830 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "starboard/common/optional.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InSequence;
+
+namespace starboard {
+namespace {
+
+TEST(OptionalTest, EnsureDefaultConstructorGivesDisengagedOptional) {
+  optional<int> test;
+  EXPECT_TRUE(!test);
+}
+
+TEST(OptionalTest, EnsureNullOptConstructorGivesDisengagedOptional) {
+  optional<int> test(nullopt);
+  EXPECT_TRUE(!test);
+}
+
+TEST(OptionalTest, InitializeConstructor) {
+  optional<int> test(2);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(2, test.value());
+}
+
+TEST(OptionalTest, BoolCastOperator) {
+  optional<int> test;
+  EXPECT_FALSE(static_cast<bool>(test));
+
+  test = 5;
+  EXPECT_TRUE(static_cast<bool>(test));
+}
+
+TEST(OptionalTest, InitializeAssign) {
+  optional<int> test = 2;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(2, test.value());
+}
+
+TEST(OptionalTest, ReassignValue) {
+  optional<int> test(2);
+  test = 5;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, CopyAssignment) {
+  optional<int> a;
+  optional<int> b = 2;
+  optional<int> c = 3;
+  a = b;
+  EXPECT_FALSE(!a);
+  EXPECT_EQ(2, a.value());
+
+  a = c;
+  EXPECT_FALSE(!a);
+  EXPECT_EQ(3, a.value());
+}
+
+TEST(OptionalTest, ClearAssignment) {
+  optional<int> test(2);
+  test = optional<int>();
+  EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, EnsureAssignmentOfNullOptResultsInDisengagement) {
+  optional<int> test(2);
+  test = nullopt;
+  EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, EnsureAssignmentOfDefaultOptionalResultsInDisengagement) {
+  optional<int> test(2);
+  test = optional<int>();
+  EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, CopyConstruction) {
+  optional<int> test1(2);
+  optional<int> test2(test1);
+
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(2, test2.value());
+}
+
+TEST(OptionalTest, Swap) {
+  optional<int> test1(1);
+  optional<int> test2(2);
+
+  // Swap two engaged optionals.
+  test1.swap(test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(2, test1.value());
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two optionals where only one is engaged.
+  test1 = nullopt;
+  test1.swap(test2);
+  EXPECT_FALSE(test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(1, test1.value());
+
+  // Swap two optionals where only one is engaged, except the other way around.
+  test1.swap(test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two disengaged optionals.
+  test2 = nullopt;
+  test1.swap(test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(test2);
+}
+
+TEST(OptionalTest, StdSwap) {
+  optional<int> test1(1);
+  optional<int> test2(2);
+
+  // Swap two engaged optionals.
+  std::swap(test1, test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(2, test1.value());
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two optionals where only one is engaged.
+  test1 = nullopt;
+  std::swap(test1, test2);
+  EXPECT_FALSE(test2);
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(1, test1.value());
+
+  // Swap two optionals where only one is engaged, except the other way around.
+  std::swap(test1, test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(1, test2.value());
+
+  // Swap two disengaged optionals.
+  test2 = nullopt;
+  std::swap(test1, test2);
+  EXPECT_FALSE(test1);
+  EXPECT_FALSE(test2);
+}
+
+TEST(OptionalTest, SwapWithSelf) {
+  optional<int> test(1);
+
+  test.swap(test);
+  EXPECT_EQ(1, test.value());
+
+  std::swap(test, test);
+  EXPECT_EQ(1, test.value());
+}
+
+TEST(OptionalTest, AsteriskDereference) {
+  optional<int> test(2);
+  EXPECT_EQ(2, *test);
+
+  *test = 5;
+  EXPECT_EQ(5, test.value());
+}
+
+struct TestStruct {
+  int foobar;
+};
+
+TEST(OptionalTest, ArrowDereference) {
+  TestStruct a;
+  a.foobar = 2;
+
+  optional<TestStruct> test(a);
+  EXPECT_EQ(2, test->foobar);
+
+  test->foobar = 5;
+  EXPECT_EQ(5, test.value().foobar);
+
+  // Test const arrow dereference.
+  const optional<TestStruct> test_const(a);
+  EXPECT_EQ(2, test_const->foobar);
+}
+
+class NoDefaultTest {
+ public:
+  explicit NoDefaultTest(int i) { foobar_ = i; }
+  int foobar() const { return foobar_; }
+
+ private:
+  NoDefaultTest();
+
+  int foobar_;
+};
+
+TEST(OptionalTest, NoDefaultConstructorIsSupported) {
+  // First test with an object passed in upon construction
+  optional<NoDefaultTest> test1(NoDefaultTest(2));
+  EXPECT_EQ(2, test1.value().foobar());
+
+  // Now test with an object assignment after construction
+  optional<NoDefaultTest> test2;
+  test2 = NoDefaultTest(5);
+  EXPECT_EQ(5, test2.value().foobar());
+}
+
+TEST(OptionalTest, TestEquivalenceComparisons) {
+  optional<int> test1 = 1;
+  optional<int> test2 = 2;
+  optional<int> test3;
+  optional<int> test4 = 2;
+
+  EXPECT_FALSE(test1 == test2);  // NOLINT(readability/check)
+  EXPECT_FALSE(test2 == test1);  // NOLINT(readability/check)
+  EXPECT_FALSE(test1 == test3);  // NOLINT(readability/check)
+  EXPECT_FALSE(test3 == test1);  // NOLINT(readability/check)
+  EXPECT_FALSE(2 == test1);      // NOLINT(readability/check)
+  EXPECT_FALSE(test1 == 2);      // NOLINT(readability/check)
+  EXPECT_EQ(1, test1);
+  EXPECT_EQ(test1, 1);
+  EXPECT_EQ(test4, test2);
+  EXPECT_EQ(test2, test4);
+
+  // Test nullopt comparisons
+  EXPECT_TRUE(test3 == nullopt);
+  EXPECT_TRUE(nullopt == test3);
+  EXPECT_FALSE(test1 == nullopt);
+  EXPECT_FALSE(nullopt == test1);
+}
+
+TEST(OptionalTest, TestLessThanComparisons) {
+  optional<int> test1 = 1;
+  optional<int> test2 = 2;
+  optional<int> test3;
+  optional<int> test4 = 2;
+
+  EXPECT_TRUE(test1 < test2);
+  EXPECT_FALSE(test2 < test1);
+  EXPECT_FALSE(test1 < test1);
+  EXPECT_TRUE(test3 < test1);
+  EXPECT_FALSE(test1 < test3);
+  EXPECT_FALSE(test3 < test3);
+
+  EXPECT_TRUE(nullopt < test1);
+  EXPECT_FALSE(test1 < nullopt);
+
+  EXPECT_TRUE(test1 < 2);   // NOLINT(readability/check)
+  EXPECT_FALSE(2 < test1);  // NOLINT(readability/check)
+}
+
+TEST(OptionalTest, EnsureOptionalWorksWithFunnyAlignments) {
+  struct {
+    char c;
+    optional<uint64_t> number;
+  } foo;
+
+  EXPECT_FALSE(!!foo.number);
+  foo.number = 1;
+  EXPECT_TRUE(!!foo.number);
+  EXPECT_EQ(1, foo.number.value());
+}
+
+class DestructorCallTester {
+ public:
+  DestructorCallTester() {}
+  DestructorCallTester(const DestructorCallTester&) {}
+  ~DestructorCallTester() { Die(); }
+  MOCK_METHOD0(Die, void());
+};
+
+TEST(OptionalTest, EnsureDestructorIsCalled) {
+  {
+    // Ensure destructor is called upon optional destruction
+    optional<DestructorCallTester> test(in_place);
+    EXPECT_CALL(test.value(), Die());
+  }
+
+  {
+    // Ensure destructor is called upon assignment to null
+    optional<DestructorCallTester> test1(in_place);
+    optional<DestructorCallTester> test2(in_place);
+    {
+      InSequence s;
+      EXPECT_CALL(test1.value(), Die());
+      EXPECT_CALL(test2.value(), Die());
+    }
+    test1 = nullopt;
+  }
+}
+
+// This class counts all calls to the set of methods declared in the class.
+// It can be used to verify that certain methods are indeed called.
+class MethodCallCounter {
+ public:
+  MethodCallCounter() {
+    ResetCounts();
+    ++default_constructor_calls_;
+  }
+
+  MethodCallCounter(const MethodCallCounter& other) {
+    // A very non-standard copy constructor, since this is a test object
+    // intended to count method calls on this object and only this object.
+    ResetCounts();
+    ++copy_constructor_calls_;
+  }
+
+  MethodCallCounter(MethodCallCounter&& other) {  // NOLINT(build/c++11)
+    // A very non-standard move constructor, since this is a test object
+    // intended to count method calls on this object and only this object.
+    ResetCounts();
+    ++move_constructor_calls_;
+  }
+
+  MethodCallCounter& operator=(const MethodCallCounter& other) {
+    ++assignment_calls_;
+    return *this;
+  }
+
+  // NOLINTNEXTLINE(build/c++11)
+  MethodCallCounter& operator=(MethodCallCounter&& other) {
+    ++move_assignment_calls_;
+    return *this;
+  }
+
+  int assignment_calls() const { return assignment_calls_; }
+  int copy_constructor_calls() const { return copy_constructor_calls_; }
+  int default_constructor_calls() const { return default_constructor_calls_; }
+  int move_assignment_calls() const { return move_assignment_calls_; }
+  int move_constructor_calls() const { return move_constructor_calls_; }
+
+ private:
+  void ResetCounts() {
+    assignment_calls_ = 0;
+    copy_constructor_calls_ = 0;
+    default_constructor_calls_ = 0;
+    move_assignment_calls_ = 0;
+    move_constructor_calls_ = 0;
+  }
+
+  int assignment_calls_;
+  int copy_constructor_calls_;
+  int default_constructor_calls_;
+  int move_assignment_calls_;
+  int move_constructor_calls_;
+};
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueCopyConstructor) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(original_counter);
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(1, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByOptionalCopyConstructor) {
+  optional<MethodCallCounter> test1(in_place);
+  optional<MethodCallCounter> test2(test1);
+
+  EXPECT_EQ(0, test2->assignment_calls());
+  EXPECT_EQ(1, test2->copy_constructor_calls());
+  EXPECT_EQ(0, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByOptionalAssignment) {
+  optional<MethodCallCounter> test1(in_place);
+  optional<MethodCallCounter> test2;
+  test2 = test1;
+
+  EXPECT_EQ(0, test2->assignment_calls());
+  EXPECT_EQ(1, test2->copy_constructor_calls());
+  EXPECT_EQ(0, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+TEST(OptionalTest, AssignmentIsCalledByValueAssignment) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(in_place);
+  test = original_counter;
+
+  EXPECT_EQ(1, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, AssignmentIsCalledByOptionalAssignment) {
+  optional<MethodCallCounter> test1(in_place);
+  optional<MethodCallCounter> test2(in_place);
+  test2 = test1;
+
+  EXPECT_EQ(1, test2->assignment_calls());
+  EXPECT_EQ(0, test2->copy_constructor_calls());
+  EXPECT_EQ(1, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnOptionalMoveConstructor) {
+  optional<MethodCallCounter> test(
+      std::move(optional<MethodCallCounter>(in_place)));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnUnengagedOptionalMoveAssignment) {
+  optional<MethodCallCounter> test;
+  test = std::move(optional<MethodCallCounter>(in_place));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveAssignmentIsCalledOnEngagedMoveOptionalAssignment) {
+  optional<MethodCallCounter> test(in_place);
+  test = std::move(optional<MethodCallCounter>(in_place));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(1, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnUnengagedMoveValueConstructor) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(std::move(original_counter));
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveConstructorIsCalledOnUnengagedMoveValueAssignment) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test;
+  test = std::move(original_counter);
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(1, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, MoveAssignmentIsCalledOnEngagedMoveValueAssignment) {
+  MethodCallCounter original_counter;
+  optional<MethodCallCounter> test(in_place);
+  test = std::move(original_counter);
+
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(1, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+TEST(OptionalTest, EnsureSelfAssignmentIsOkay) {
+  // Ensure that values are as we expect them to be.
+  optional<int> test1(5);
+
+  test1 = test1;
+
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(5, test1.value());
+
+  // Ensure that the methods we expect to be called are actually called.
+  optional<MethodCallCounter> test2(in_place);
+
+  test2 = test2;
+
+  EXPECT_EQ(1, test2->assignment_calls());
+  EXPECT_EQ(0, test2->copy_constructor_calls());
+  EXPECT_EQ(1, test2->default_constructor_calls());
+  EXPECT_EQ(0, test2->move_assignment_calls());
+  EXPECT_EQ(0, test2->move_constructor_calls());
+}
+
+// Helper classes to ensure that we can assign different values to optionals
+// so long as the wrapped value can be assigned and constructed from the other.
+struct XType {
+  explicit XType(int number) { number_ = number; }
+
+  int number_;
+};
+
+struct YType {
+  explicit YType(int number) { number_ = number; }
+
+  explicit YType(const XType& x_type) { number_ = x_type.number_; }
+
+  YType& operator=(const XType& x_type) {
+    number_ = x_type.number_;
+    return *this;
+  }
+
+  int number_;
+};
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueAssignmentFromOtherType) {
+  XType x_type(1);
+  optional<YType> test;
+  test = x_type;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->number_);
+}
+
+TEST(OptionalTest, AssignmentIsCalledByValueAssignmentFromOtherType) {
+  XType x_type(1);
+  optional<YType> test(in_place, 2);
+  test = x_type;
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->number_);
+}
+
+TEST(OptionalTest, TestMakeOptional) {
+  optional<int> test = make_optional(5);
+
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, ValueOrTest) {
+  optional<int> test;
+
+  EXPECT_EQ(4, test.value_or(4));
+
+  test = 2;
+
+  EXPECT_EQ(2, test.value_or(4));
+}
+
+TEST(OptionalTest, ConstOptionalsTest) {
+  const optional<int> test1(5);
+
+  EXPECT_FALSE(!test1);
+  EXPECT_EQ(5, test1.value());
+  EXPECT_EQ(5, *test1);
+
+  const optional<int> test2(test1);
+
+  EXPECT_FALSE(!test2);
+  EXPECT_EQ(5, test2.value());
+  EXPECT_EQ(5, *test2);
+
+  optional<int> test3;
+  test3 = test2;
+
+  EXPECT_FALSE(!test3);
+  EXPECT_EQ(5, test3.value());
+  EXPECT_EQ(5, *test3);
+}
+
+TEST(OptionalTest, EmplaceInt) {
+  optional<int> test;
+
+  test.emplace(5);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, EmplaceWithDefaultConstructor) {
+  optional<MethodCallCounter> test;
+  test.emplace();
+
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(0, test->assignment_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(0, test->move_assignment_calls());
+  EXPECT_EQ(0, test->move_constructor_calls());
+}
+
+class NoDefaultOrCopyConstructor {
+ public:
+  NoDefaultOrCopyConstructor(int x, int y) {
+    x_ = x;
+    y_ = y;
+  }
+
+  int x() const { return x_; }
+  int y() const { return y_; }
+
+ private:
+  NoDefaultOrCopyConstructor();
+  NoDefaultOrCopyConstructor(const NoDefaultOrCopyConstructor&);
+
+  int x_;
+  int y_;
+};
+
+TEST(OptionalTest, EmplaceObjectWithNoDefaultOrCopyConstructor) {
+  optional<NoDefaultOrCopyConstructor> test;
+  test.emplace(1, 2);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->x());
+  EXPECT_EQ(2, test->y());
+}
+
+class NonConstPointerInConstructor {
+ public:
+  explicit NonConstPointerInConstructor(int* param) { param_ = param; }
+
+  void SetParam(int value) { *param_ = value; }
+
+ private:
+  int* param_;
+};
+
+TEST(OptionalTest, EmplaceObjectWithNonConstPointer) {
+  int value = 0;
+  optional<NonConstPointerInConstructor> test;
+  test.emplace(&value);
+  test->SetParam(5);
+  EXPECT_EQ(5, value);
+}
+
+TEST(OptionalTest, ForwardingConstructorInt) {
+  optional<int> test(in_place, 5);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, ForwardingConstructorWithDefaultConstructor) {
+  optional<MethodCallCounter> test(in_place);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->default_constructor_calls());
+  EXPECT_EQ(0, test->copy_constructor_calls());
+  EXPECT_EQ(0, test->assignment_calls());
+}
+
+TEST(OptionalTest, ForwardingConstructorObjectWithNoDefaultOrCopyConstructor) {
+  optional<NoDefaultOrCopyConstructor> test(in_place, 1, 2);
+  EXPECT_FALSE(!test);
+  EXPECT_EQ(1, test->x());
+  EXPECT_EQ(2, test->y());
+}
+
+TEST(OptionalTest, ForwardingConstructorObjectWithNonConstPointer) {
+  int value = 0;
+  optional<NonConstPointerInConstructor> test(in_place, &value);
+  test->SetParam(5);
+  EXPECT_EQ(5, value);
+}
+
+TEST(OptionalTest, OptionalStringTest) {
+  {
+    optional<std::string> test;
+    EXPECT_TRUE(!test);
+
+    test = std::string("foo");
+    EXPECT_STREQ("foo", test->c_str());
+  }
+
+  {
+    optional<std::string> test(std::string("foo"));
+    EXPECT_FALSE(!test);
+
+    EXPECT_STREQ("foo", test->c_str());
+    optional<std::string> test2(test);
+    EXPECT_STREQ("foo", test2->c_str());
+  }
+}
+
+TEST(OptionalTest, OptionalStringInSetTest) {
+  // Optional strings in map test
+  std::set<optional<std::string>> optional_string_set;
+
+  optional_string_set.insert(std::string("foo"));
+  optional_string_set.insert(std::string("bar"));
+
+  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+              optional_string_set.end());
+  EXPECT_FALSE(optional_string_set.find(optional<std::string>()) !=
+               optional_string_set.end());
+
+  optional_string_set.insert(optional<std::string>());
+
+  EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(optional<std::string>()) !=
+              optional_string_set.end());
+}
+
+TEST(OptionalTest, StdVectorOfOptionalsWorksFine) {
+  std::vector<optional<int>> test_vector;
+
+  test_vector.reserve(test_vector.capacity() + 1);
+  test_vector.resize(test_vector.capacity());
+
+  // Make sure all current optionals are disengaged.
+  for (std::vector<optional<int>>::const_iterator iter = test_vector.begin();
+       iter != test_vector.end(); ++iter) {
+    EXPECT_TRUE(!*iter);
+  }
+  EXPECT_TRUE(!test_vector[0]);
+
+  test_vector.clear();
+  test_vector.resize(test_vector.capacity() + 1, 5);
+  for (std::vector<optional<int>>::const_iterator iter = test_vector.begin();
+       iter != test_vector.end(); ++iter) {
+    ASSERT_FALSE(!*iter);
+    EXPECT_EQ(5, **iter);
+  }
+  ASSERT_FALSE(!test_vector[0]);
+  EXPECT_EQ(5, *test_vector[0]);
+
+  test_vector.push_back(8);
+  ASSERT_FALSE(!test_vector.back());
+  EXPECT_EQ(8, *test_vector.back());
+}
+
+TEST(OptionalTest, OptionalStringInUnorderedSet) {
+  std::unordered_set<optional<std::string>> optional_string_set;
+
+  optional_string_set.insert(std::string());
+  optional_string_set.insert(std::string("7"));
+  optional_string_set.insert(std::string("123456"));
+
+  EXPECT_TRUE(optional_string_set.find(std::string("7")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("123456")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string()) !=
+              optional_string_set.end());
+  EXPECT_FALSE(optional_string_set.find(optional<std::string>()) !=
+               optional_string_set.end());
+
+  optional_string_set.insert(optional<std::string>());
+
+  EXPECT_TRUE(optional_string_set.find(std::string("7")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string("123456")) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(std::string()) !=
+              optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(optional<std::string>()) !=
+              optional_string_set.end());
+
+  optional_string_set.erase(std::string());
+
+  EXPECT_FALSE(optional_string_set.find(std::string()) !=
+               optional_string_set.end());
+  EXPECT_TRUE(optional_string_set.find(optional<std::string>()) !=
+              optional_string_set.end());
+}
+
+TEST(OptionalTest, OptionalIntInUnorderedSet) {
+  std::unordered_set<optional<int>> optional_int_set;
+
+  optional_int_set.insert(0);
+  optional_int_set.insert(7);
+  optional_int_set.insert(123456);
+
+  EXPECT_TRUE(optional_int_set.find(7) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(123456) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(0) != optional_int_set.end());
+  EXPECT_FALSE(optional_int_set.find(optional<int>()) !=
+               optional_int_set.end());
+
+  optional_int_set.insert(optional<int>());
+
+  EXPECT_TRUE(optional_int_set.find(7) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(123456) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(0) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(optional<int>()) != optional_int_set.end());
+
+  optional_int_set.erase(0);
+
+  EXPECT_FALSE(optional_int_set.find(0) != optional_int_set.end());
+  EXPECT_TRUE(optional_int_set.find(optional<int>()) != optional_int_set.end());
+}
+
+optional<int> ConditionallyMakeOptional(bool should_make, int value) {
+  if (should_make) {
+    return optional<int>(value);
+  } else {
+    return nullopt;
+  }
+}
+
+TEST(OptionalTest, OptionalCanBeReturnedFromFunction) {
+  optional<int> test1 = ConditionallyMakeOptional(true, 5);
+  ASSERT_FALSE(!test1);
+  EXPECT_EQ(5, test1.value());
+
+  optional<int> test2 = ConditionallyMakeOptional(false, 5);
+  EXPECT_TRUE(!test2);
+}
+
+}  // namespace
+}  // namespace starboard
diff --git a/src/starboard/nplb/state_machine_test.cc b/src/starboard/nplb/state_machine_test.cc
new file mode 100644
index 0000000..86888c0
--- /dev/null
+++ b/src/starboard/nplb/state_machine_test.cc
@@ -0,0 +1,740 @@
+// Copyright (c) 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "starboard/common/state_machine.h"
+
+#include <iostream>
+#include <list>
+
+#include "starboard/log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Enables some verbose logging for debugging the state machine test and
+// implementation.
+#define STATE_MACHINE_TEST_DEBUG 0
+
+namespace starboard {
+namespace {
+namespace hsm {
+
+// Constant to represent no version for TestHsm::Expect.
+static const uint64_t kNoVersion = static_cast<uint64_t>(-1);
+
+// States for the test HSM
+enum TestState {
+  kStateS0,
+  kStateS1,
+  kStateS11,
+  kStateS2,
+  kStateS21,
+  kStateS211,
+  kStateT0,
+};
+
+// Events for the test HSM
+enum TestEvent {
+  kEventA,
+  kEventB,
+  kEventC,
+  kEventD,
+  kEventE,
+  kEventF,
+  kEventG,
+  kEventH,
+  kEventI,
+  kEventJ,
+};
+
+// An enumeration of things that the HSM does that we can sense and then
+// assert about.
+enum HsmEvent { kHsmEnter, kHsmExit, kHsmHandled };
+
+}  // namespace hsm
+
+// --- Test Subclass ---
+
+// StateMachine is an abstract class, so we must subclass it to test it.
+// This class uses the sample test state machine specified by Miro Samek in his
+// Practical Statecharts book. It covers the interesting transitions and
+// topologies, so if it's fully exercised, it should represent a
+// close-to-canonical test of the state machine's facilities.
+//
+// The diagram of this statechart is reproduced here:
+// http://www.state-machine.com/resources/Heinzmann04.pdf
+//
+// This version has:
+//  - A new event, I, in state S11, to test reentrant event handling.
+//  - A new event, J, and new state T0, to test the no top state case.
+class TestHsm : public StateMachine<hsm::TestState, hsm::TestEvent> {
+ public:
+  // --- Some types to aid sensing ---
+
+  struct ResultEvent {
+    // The state the HSM was in when it handled the event.
+    const optional<hsm::TestState> state;
+
+    // The data passed into the event, if any.
+    const void* data;
+
+    // The state that actually handled the event (could be an ancestor of the
+    // current state.
+    const optional<hsm::TestState> event_state;
+
+    // The event that was handled.
+    const optional<hsm::TestEvent> event;
+
+    // The "HSM Event" that occurred causing this to be recorded.
+    const hsm::HsmEvent hsm_event;
+
+    // The state version at the time the HsmEvent occured.
+    const uint64_t version;
+  };
+
+  // --- Extra state to aid sensing ---
+
+  // Foo is used as extended state and in guard conditions in the test HSM (see
+  // link above).
+  bool foo_;
+
+  // A counter for how many times the S11 I handler is invoked.
+  int event_i_count_;
+
+  // A collection of interesting things that has happened to this state
+  // machine. Used to validate that the HSM behaved as expected.
+  std::list<ResultEvent> results;
+
+  TestHsm()
+      : StateMachine<hsm::TestState, hsm::TestEvent>("TestHsm"),
+        foo_(true),
+        event_i_count_(0) {
+#if STATE_MACHINE_TEST_DEBUG
+    EnableLogging();
+#endif
+  }
+  virtual ~TestHsm() {}
+
+  // Clears the results list, so nothing bleeds between test cases.
+  void ClearResults() { results.clear(); }
+
+  // Consumes and validates a ResultEvent from the results list.
+  void Expect(hsm::HsmEvent hsm_event,
+              optional<hsm::TestState> event_state = nullopt,
+              optional<hsm::TestEvent> event = nullopt,
+              optional<hsm::TestState> current_state = nullopt,
+              void* data = NULL,
+              uint64_t version = hsm::kNoVersion) {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << ": hsm_event=" << hsm_event
+                  << ", event_state=" << GetStateString(event_state)
+                  << ", event=" << GetEventString(event)
+                  << ", current_state=" << GetStateString(current_state)
+                  << ", data=0x" << std::hex << data << ", version=" << version;
+#endif  // STATE_MACHINE_TEST_DEBUG
+
+    EXPECT_FALSE(results.empty());
+    TestHsm::ResultEvent result = results.front();
+    results.pop_front();
+    EXPECT_EQ(hsm_event, result.hsm_event);
+    if (event_state) {
+      EXPECT_EQ(event_state, result.event_state);
+      if (!current_state) {
+        EXPECT_EQ(event_state, result.state);
+      }
+    }
+
+    if (current_state) {
+      EXPECT_EQ(current_state, result.state);
+    }
+
+    if (event) {
+      EXPECT_EQ(event, result.event);
+    }
+    EXPECT_EQ(data, result.data);
+
+    if (version != hsm::kNoVersion) {
+      EXPECT_EQ(version, result.version);
+    }
+  }
+
+  // --- StateMachine Implementation ---
+ protected:
+  virtual optional<hsm::TestState> GetUserParentState(
+      hsm::TestState state) const SB_OVERRIDE {
+    switch (state) {
+      case hsm::kStateS1:
+      case hsm::kStateS2:
+        return hsm::kStateS0;
+      case hsm::kStateS11:
+        return hsm::kStateS1;
+      case hsm::kStateS21:
+        return hsm::kStateS2;
+      case hsm::kStateS211:
+        return hsm::kStateS21;
+      default:
+        return nullopt;
+    }
+  }
+
+  virtual optional<hsm::TestState> GetUserInitialSubstate(
+      hsm::TestState state) const SB_OVERRIDE {
+    switch (state) {
+      case hsm::kStateS0:
+        return hsm::kStateS1;
+      case hsm::kStateS1:
+        return hsm::kStateS11;
+      case hsm::kStateS2:
+        return hsm::kStateS21;
+      case hsm::kStateS21:
+        return hsm::kStateS211;
+      default:
+        return nullopt;
+    }
+  }
+
+  virtual hsm::TestState GetUserInitialState() const SB_OVERRIDE {
+    return hsm::kStateS0;
+  }
+
+  virtual const char* GetUserStateString(hsm::TestState state) const
+      SB_OVERRIDE {
+    switch (state) {
+      case hsm::kStateS0:
+        return "S0";
+      case hsm::kStateS1:
+        return "S1";
+      case hsm::kStateS11:
+        return "S11";
+      case hsm::kStateS2:
+        return "S2";
+      case hsm::kStateS21:
+        return "S21";
+      case hsm::kStateS211:
+        return "S211";
+      case hsm::kStateT0:
+        return "T0";
+      default:
+        return NULL;
+    }
+  }
+
+  virtual const char* GetUserEventString(hsm::TestEvent event) const
+      SB_OVERRIDE {
+    switch (event) {
+      case hsm::kEventA:
+        return "A";
+      case hsm::kEventB:
+        return "B";
+      case hsm::kEventC:
+        return "C";
+      case hsm::kEventD:
+        return "D";
+      case hsm::kEventE:
+        return "E";
+      case hsm::kEventF:
+        return "F";
+      case hsm::kEventG:
+        return "G";
+      case hsm::kEventH:
+        return "H";
+      case hsm::kEventI:
+        return "I";
+      case hsm::kEventJ:
+        return "J";
+      default:
+        return NULL;
+    }
+  }
+
+  virtual Result HandleUserStateEvent(hsm::TestState state,
+                                      hsm::TestEvent event,
+                                      void* data) SB_OVERRIDE {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << "(" << GetStateString(state) << ", "
+                  << GetEventString(event) << ", 0x" << std::hex << data
+                  << ");";
+#endif  // STATE_MACHINE_TEST_DEBUG
+
+    Result result(kNotHandled);
+    switch (state) {
+      case hsm::kStateS0:
+        switch (event) {
+          case hsm::kEventE:
+            result = hsm::kStateS211;
+            break;
+          case hsm::kEventJ:
+            result = hsm::kStateT0;
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      case hsm::kStateS1:
+        switch (event) {
+          case hsm::kEventA:
+            result = Result(hsm::kStateS1, true);
+            break;
+          case hsm::kEventB:
+            result = hsm::kStateS11;
+            break;
+          case hsm::kEventC:
+            result = hsm::kStateS2;
+            break;
+          case hsm::kEventD:
+            result = hsm::kStateS0;
+            break;
+          case hsm::kEventF:
+            result = hsm::kStateS211;
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      case hsm::kStateS11:
+        switch (event) {
+          case hsm::kEventG:
+            result = hsm::kStateS211;
+            break;
+          case hsm::kEventH:
+            if (foo_) {
+              foo_ = false;
+              result = kHandled;
+              break;
+            }
+            break;
+          case hsm::kEventI:
+            // Inject another I every other time I is handled so every I should
+            // ultimately increase event_i_count_ by 2.
+            ++event_i_count_;
+            if (event_i_count_ % 2 == 1) {
+              // This should queue and be run before Handle() returns.
+              Handle(hsm::kEventI);
+              result = hsm::kStateS1;
+              break;
+            } else {
+              result = kHandled;
+              break;
+            }
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      case hsm::kStateS2:
+        switch (event) {
+          case hsm::kEventC:
+            result = hsm::kStateS1;
+            break;
+          case hsm::kEventF:
+            result = hsm::kStateS11;
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      case hsm::kStateS21:
+        switch (event) {
+          case hsm::kEventB:
+            result = hsm::kStateS211;
+            break;
+          case hsm::kEventH:
+            if (!foo_) {
+              foo_ = true;
+              result = Result(hsm::kStateS21, true);
+              break;
+            }
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      case hsm::kStateS211:
+        switch (event) {
+          case hsm::kEventD:
+            result = hsm::kStateS21;
+            break;
+          case hsm::kEventG:
+            result = hsm::kStateS0;
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      case hsm::kStateT0:
+        switch (event) {
+          case hsm::kEventJ:
+            result = hsm::kStateS0;
+            break;
+          default:
+            // Not Handled
+            break;
+        }
+        break;
+
+      default:
+        // Not Handled
+        break;
+    }
+
+    if (result.is_handled) {
+      AddEvent(state, event, data, hsm::kHsmHandled);
+    }
+
+    return result;
+  }
+
+  virtual void HandleUserStateEnter(hsm::TestState state) SB_OVERRIDE {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << "(" << GetStateString(state)
+                  << ", ENTER);";
+#endif  // STATE_MACHINE_TEST_DEBUG
+
+    AddEvent(state, nullopt, NULL, hsm::kHsmEnter);
+  }
+
+  virtual void HandleUserStateExit(hsm::TestState state) SB_OVERRIDE {
+#if STATE_MACHINE_TEST_DEBUG
+    SB_DLOG(INFO) << __FUNCTION__ << "(" << GetStateString(state) << ", EXIT);";
+#endif  // STATE_MACHINE_TEST_DEBUG
+
+    AddEvent(state, nullopt, NULL, hsm::kHsmExit);
+  }
+
+ private:
+  // Adds a new record to the result list.
+  void AddEvent(hsm::TestState state,
+                optional<hsm::TestEvent> event,
+                void* data,
+                hsm::HsmEvent hsm_event) {
+    ResultEvent result_event = {this->state(), data,      state,
+                                event,         hsm_event, this->version()};
+    results.push_back(result_event);
+  }
+};
+
+// --- Test Definitions ---
+
+// This test validates that a state machine will initialize itself when it
+// handles its first event, even if the user has not explicitly called
+// initialize.
+TEST(StateMachineTest, AutoInit) {
+  TestHsm hsm;
+
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventA);
+
+  // The HSM should Auto-Initialize
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS0);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+
+  // Then it should handle the event
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventA, hsm::kStateS11, 0);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(1, hsm.version());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+}
+
+// This test validates that if Handle() is called from within an event handler,
+// it queues the event rather than trying to Handle the next event
+// reentrantly. This behavior, or something like it, is required to make the
+// state machine truly run-to-completion.
+TEST(StateMachineTest, ReentrantHandle) {
+  TestHsm hsm;
+  hsm.Initialize();
+  EXPECT_EQ(0, hsm.version());
+  hsm.ClearResults();
+
+  // Test a Handle() inside Handle()
+  EXPECT_EQ(0, hsm.event_i_count_);
+  hsm.Handle(hsm::kEventI);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventI, hsm::kStateS11,
+             NULL, 0);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventI, hsm::kStateS11,
+             NULL, 1);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(1, hsm.version());
+  EXPECT_EQ(2, hsm.event_i_count_);
+}
+
+// This test validates that every meaningful event in every state in the test
+// state machine behaves as expected. This should cover all normal operation of
+// the state machine framework, except what is extracted into their own test
+// cases above.
+TEST(StateMachineTest, KitNKaboodle) {
+  TestHsm hsm;
+
+  // Test the initial state
+  EXPECT_EQ(0, hsm.version());
+  hsm.Initialize();
+  EXPECT_EQ(0, hsm.version());
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS0);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // Test IsIn
+  EXPECT_TRUE(hsm.IsIn(hsm::kStateS11));
+  EXPECT_TRUE(hsm.IsIn(hsm::kStateS1));
+  EXPECT_TRUE(hsm.IsIn(hsm::kStateS0));
+  EXPECT_FALSE(hsm.IsIn(hsm::kStateS2));
+  EXPECT_FALSE(hsm.IsIn(hsm::kStateS21));
+  EXPECT_FALSE(hsm.IsIn(hsm::kStateS211));
+
+  // State: S11, Event: A
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventA);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventA, hsm::kStateS11, 0);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(1, hsm.version());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: B
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventB);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventB, hsm::kStateS11,
+             NULL, 1);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(2, hsm.version());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: D
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventD);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventD, hsm::kStateS11,
+             NULL, 2);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(3, hsm.version());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: H (foo == true)
+  hsm.ClearResults();
+  EXPECT_TRUE(hsm.foo_);
+  hsm.Handle(hsm::kEventH);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventH, hsm::kStateS11,
+             NULL, 3);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(3, hsm.version());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+  EXPECT_FALSE(hsm.foo_);
+
+  // State: S11, Event: H (foo == false)
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventH);
+  EXPECT_FALSE(hsm.foo_);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(3, hsm.version());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: C
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventC);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventC, hsm::kStateS11,
+             NULL, 3);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(4, hsm.version());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: A
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventA);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: B
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventB);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS21, hsm::kEventB, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: D
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventD);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS211, hsm::kEventD, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: E
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventE);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS0, hsm::kEventE, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: F
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventF);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS2, hsm::kEventF, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: E
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventE);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS0, hsm::kEventE, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: G
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventG);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS211, hsm::kEventG, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: F
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventF);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventF, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: H (foo == false)
+  EXPECT_FALSE(hsm.foo_);
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventH);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS21, hsm::kEventH, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_TRUE(hsm.foo_);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: H (foo == true)
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventH);
+  EXPECT_TRUE(hsm.foo_);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: C
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventC);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS2, hsm::kEventC, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // State: S11, Event: G
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventG);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventG, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+  // State: S211, Event: J
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventJ);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS0, hsm::kEventJ, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS0);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateT0);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateT0, hsm.state());
+
+  // State: T0, Event: J
+  hsm.ClearResults();
+  hsm.Handle(hsm::kEventJ);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateT0, hsm::kEventJ);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateT0);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS0);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+  // Test that event data gets passed through to the handler
+  hsm.ClearResults();
+  void* data = reinterpret_cast<void*>(7);
+  hsm.Handle(hsm::kEventC, data);
+  hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventC, hsm::kStateS11,
+             data);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+  hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+  hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+  EXPECT_EQ(0, hsm.results.size());
+  EXPECT_EQ(hsm::kStateS211, hsm.state());
+}
+
+}  // namespace
+}  // namespace starboard
diff --git a/src/starboard/nplb/storage_delete_record_test.cc b/src/starboard/nplb/storage_delete_record_test.cc
index 16ade12..ef0fc73 100644
--- a/src/starboard/nplb/storage_delete_record_test.cc
+++ b/src/starboard/nplb/storage_delete_record_test.cc
@@ -14,6 +14,7 @@
 
 // Sunny Day cases tested in the read/write tests.
 
+#include "starboard/nplb/file_helpers.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,10 +23,25 @@
 namespace nplb {
 namespace {
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
+TEST(SbStorageDeleteRecordTest, RainyDayInvalidUserValidName) {
+  EXPECT_FALSE(SbStorageDeleteRecord(
+      kSbUserInvalid, ScopedRandomFile::MakeRandomFilename().c_str()));
+}
+
+TEST(SbStorageDeleteRecordTest, RainyDayInvalidUserNullName) {
+  EXPECT_FALSE(SbStorageDeleteRecord(kSbUserInvalid, NULL));
+}
+
+#else  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 TEST(SbStorageDeleteRecordTest, RainyDayInvalidUser) {
   EXPECT_FALSE(SbStorageDeleteRecord(kSbUserInvalid));
 }
 
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/storage_helpers.h b/src/starboard/nplb/storage_helpers.h
index efc422e..c587203 100644
--- a/src/starboard/nplb/storage_helpers.h
+++ b/src/starboard/nplb/storage_helpers.h
@@ -28,23 +28,49 @@
 
 // Deletes the storage for the current user.
 static SB_C_INLINE void ClearStorageRecord() {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  SbStorageDeleteRecord(SbUserGetCurrent(), NULL);
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   SbStorageDeleteRecord(SbUserGetCurrent());
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
 }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+// Deletes the named storage record for the current user.
+static SB_C_INLINE void ClearStorageRecord(const char* name) {
+  SbStorageDeleteRecord(SbUserGetCurrent(), name);
+}
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 // Opens the storage record for the current user, validating that it is valid.
 static SB_C_INLINE SbStorageRecord OpenStorageRecord() {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  SbStorageRecord record = SbStorageOpenRecord(SbUserGetCurrent(), NULL);
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   SbStorageRecord record = SbStorageOpenRecord(SbUserGetCurrent());
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   EXPECT_TRUE(SbStorageIsValidRecord(record));
   return record;
 }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+// Opens the named storage record for the current user, validating that it is
+// valid.
+static SB_C_INLINE SbStorageRecord OpenStorageRecord(const char* name) {
+  SbStorageRecord record = SbStorageOpenRecord(SbUserGetCurrent(), name);
+  EXPECT_TRUE(SbStorageIsValidRecord(record));
+  return record;
+}
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 // Writes a standard pattern of |size| bytes into the given open storage
 // |record|.
 static SB_C_INLINE void WriteStorageRecord(SbStorageRecord record,
-                                           int64_t size) {
+                                           int64_t size,
+                                           int64_t pattern_offset = 0) {
   char* data = new char[size];
   for (int64_t i = 0; i < size; ++i) {
-    data[i] = static_cast<char>(i + 2 % 0xFF);
+    data[i] = static_cast<char>((i + pattern_offset + 2) % 0xFF);
   }
   EXPECT_TRUE(SbStorageWriteRecord(record, data, size));
   EXPECT_EQ(size, SbStorageGetRecordSize(record));
@@ -65,15 +91,18 @@
 // WriteStorageRecord) to start at |offset| and continue for |length|, and the
 // rest of the buffer, before and after, should be set to 0.
 static SB_C_INLINE void CheckStorageBuffer(char* data,
-                                    int64_t offset,
-                                    int64_t length,
-                                    int64_t total) {
+                                           int64_t offset,
+                                           int64_t length,
+                                           int64_t total,
+                                           int64_t pattern_offset = 0) {
   for (int64_t i = 0; i < offset; ++i) {
     EXPECT_EQ(0, data[i]) << "i = " << i;
   }
 
   for (int64_t i = 0; i < length; ++i) {
-    EXPECT_EQ(static_cast<char>(i + 2 % 0xFF), data[i + offset]) << "i=" << i;
+    EXPECT_EQ(static_cast<char>((i + pattern_offset + 2) % 0xFF),
+              data[i + offset])
+        << "i=" << i;
   }
 
   for (int64_t i = length + offset; i < total; ++i) {
@@ -86,15 +115,16 @@
 // read bytes is |expected_length| and then checks the buffer for the expected
 // pattern written in WriteStorageRecord over the expected range of the buffer.
 static SB_C_INLINE void ReadAndCheckStorage(SbStorageRecord record,
-                                     int64_t offset,
-                                     int64_t expected_length,
-                                     int64_t length,
-                                     int64_t total) {
+                                            int64_t offset,
+                                            int64_t expected_length,
+                                            int64_t length,
+                                            int64_t total,
+                                            int64_t pattern_offset = 0) {
   char* data = new char[total];
   SbMemorySet(data, 0, total);
   EXPECT_EQ(expected_length,
             SbStorageReadRecord(record, data + offset, length));
-  CheckStorageBuffer(data, offset, expected_length, total);
+  CheckStorageBuffer(data, offset, expected_length, total, pattern_offset);
   delete[] data;
 }
 
diff --git a/src/starboard/nplb/storage_open_record_test.cc b/src/starboard/nplb/storage_open_record_test.cc
index ec1e588..c935e4a 100644
--- a/src/starboard/nplb/storage_open_record_test.cc
+++ b/src/starboard/nplb/storage_open_record_test.cc
@@ -14,6 +14,7 @@
 
 // Sunny Day cases tested in the read/write tests.
 
+#include "starboard/nplb/file_helpers.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,10 +23,26 @@
 namespace nplb {
 namespace {
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
+TEST(SbStorageOpenRecordTest, RainyDayInvalidUserValidName) {
+  EXPECT_FALSE(SbStorageIsValidRecord(SbStorageOpenRecord(
+      kSbUserInvalid, ScopedRandomFile::MakeRandomFilename().c_str())));
+}
+
+TEST(SbStorageOpenRecordTest, RainyDayInvalidUserNullName) {
+  EXPECT_FALSE(
+      SbStorageIsValidRecord(SbStorageOpenRecord(kSbUserInvalid, NULL)));
+}
+
+#else  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 TEST(SbStorageOpenRecordTest, RainyDayInvalidUser) {
   EXPECT_FALSE(SbStorageIsValidRecord(SbStorageOpenRecord(kSbUserInvalid)));
 }
 
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/storage_read_record_test.cc b/src/starboard/nplb/storage_read_record_test.cc
index 4f11a0d..336e716 100644
--- a/src/starboard/nplb/storage_read_record_test.cc
+++ b/src/starboard/nplb/storage_read_record_test.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/nplb/file_helpers.h"
 #include "starboard/nplb/storage_helpers.h"
 #include "starboard/storage.h"
 #include "starboard/user.h"
@@ -22,19 +23,26 @@
 namespace {
 
 TEST(SbStorageReadRecordTest, SunnyDay) {
+  int64_t pattern = 0;
   ClearStorageRecord();
 
   SbStorageRecord record = OpenStorageRecord();
-  WriteStorageRecord(record, kStorageSize);
+  WriteStorageRecord(record, kStorageSize, pattern);
   ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
-                      kStorageSize2);
+                      kStorageSize2, pattern);
+  pattern = 6;
+  // Write different data and check again.
+  WriteStorageRecord(record, kStorageSize, pattern);
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
   EXPECT_TRUE(SbStorageCloseRecord(record));
 
   // Now we'll open again and make sure it is the same.
   record = OpenStorageRecord();
   ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
-                      kStorageSize2);
+                      kStorageSize2, pattern);
   EXPECT_TRUE(SbStorageCloseRecord(record));
+  ClearStorageRecord();
 }
 
 TEST(SbStorageReadRecordTest, SunnyDaySmallBuffer) {
@@ -46,12 +54,118 @@
   EXPECT_TRUE(SbStorageCloseRecord(record));
 }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+TEST(SbStorageReadRecordTest, SunnyDayNamed) {
+  int64_t pattern = 0;
+  std::string name = ScopedRandomFile::MakeRandomFilename();
+  ClearStorageRecord(name.c_str());
+
+  SbStorageRecord record = OpenStorageRecord(name.c_str());
+  WriteStorageRecord(record, kStorageSize, pattern);
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
+  // Write different data and check again.
+  pattern = 6;
+  WriteStorageRecord(record, kStorageSize, pattern);
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
+  EXPECT_TRUE(SbStorageCloseRecord(record));
+
+  // Now we'll open again and make sure it is the same.
+  record = OpenStorageRecord(name.c_str());
+  ReadAndCheckStorage(record, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern);
+  EXPECT_TRUE(SbStorageCloseRecord(record));
+
+  ClearStorageRecord(name.c_str());
+}
+
+TEST(SbStorageReadRecordTest, SunnyDayNamed2) {
+  int64_t pattern1 = 2;
+  int64_t pattern2 = 8;
+  std::string name1 = ScopedRandomFile::MakeRandomFilename();
+  std::string name2 = ScopedRandomFile::MakeRandomFilename();
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+
+  SbStorageRecord record1 = OpenStorageRecord(name1.c_str());
+  SbStorageRecord record2 = OpenStorageRecord(name2.c_str());
+  WriteStorageRecord(record1, kStorageSize, pattern1);
+  WriteStorageRecord(record2, kStorageSize, pattern2);
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+
+  // Now we'll open again and make sure they are the same.
+  record1 = OpenStorageRecord(name1.c_str());
+  record2 = OpenStorageRecord(name2.c_str());
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+}
+
+TEST(SbStorageReadRecordTest, SunnyDayNamed3) {
+  int64_t pattern1 = 3;
+  int64_t pattern2 = 5;
+  int64_t pattern3 = 7;
+  std::string name1 = ScopedRandomFile::MakeRandomFilename();
+  std::string name2 = ScopedRandomFile::MakeRandomFilename();
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+  ClearStorageRecord();
+
+  SbStorageRecord record1 = OpenStorageRecord(name1.c_str());
+  SbStorageRecord record2 = OpenStorageRecord(name2.c_str());
+  SbStorageRecord record3 = OpenStorageRecord();
+  WriteStorageRecord(record1, kStorageSize, pattern1);
+  WriteStorageRecord(record2, kStorageSize, pattern2);
+  WriteStorageRecord(record3, kStorageSize, pattern3);
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  ReadAndCheckStorage(record3, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern3);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+  EXPECT_TRUE(SbStorageCloseRecord(record3));
+
+  // Now we'll open again and make sure they are the same.
+  record1 = OpenStorageRecord(name1.c_str());
+  record2 = OpenStorageRecord(name2.c_str());
+  record3 = OpenStorageRecord();
+  ReadAndCheckStorage(record1, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern1);
+  ReadAndCheckStorage(record2, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern2);
+  ReadAndCheckStorage(record3, kStorageOffset, kStorageSize, kStorageSize2,
+                      kStorageSize2, pattern3);
+  EXPECT_TRUE(SbStorageCloseRecord(record1));
+  EXPECT_TRUE(SbStorageCloseRecord(record2));
+  EXPECT_TRUE(SbStorageCloseRecord(record3));
+
+  ClearStorageRecord(name1.c_str());
+  ClearStorageRecord(name2.c_str());
+  ClearStorageRecord();
+}
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
 TEST(SbStorageReadRecordTest, SunnyDayNonexistant) {
   ClearStorageRecord();
 
   SbStorageRecord record = OpenStorageRecord();
   ReadAndCheckStorage(record, kStorageOffset, 0, 0, kStorageSize);
   EXPECT_TRUE(SbStorageCloseRecord(record));
+  ClearStorageRecord();
 }
 
 TEST(SbStorageReadRecordTest, RainyDayInvalidRecord) {
diff --git a/src/starboard/nplb/system_get_path_test.cc b/src/starboard/nplb/system_get_path_test.cc
index ea36035..9d2bb59 100644
--- a/src/starboard/nplb/system_get_path_test.cc
+++ b/src/starboard/nplb/system_get_path_test.cc
@@ -14,6 +14,9 @@
 
 #include <string.h>
 
+#include "starboard/memory.h"
+#include "starboard/nplb/file_helpers.h"
+#include "starboard/string.h"
 #include "starboard/system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -29,15 +32,15 @@
                bool expected_result,
                int line) {
 #define LOCAL_CONTEXT "Context : id=" << id << ", line=" << line;
-  char path[kPathSize] = {0};
-  memset(path, 0xCD, kPathSize);
+  char path[kPathSize];
+  SbMemorySet(path, 0xCD, kPathSize);
   bool result = SbSystemGetPath(id, path, kPathSize);
   if (expect_result) {
     EXPECT_EQ(expected_result, result) << LOCAL_CONTEXT;
   }
   if (result) {
     EXPECT_NE('\xCD', path[0]) << LOCAL_CONTEXT;
-    int len = static_cast<int>(strlen(path));
+    int len = static_cast<int>(SbStringGetLength(path));
     EXPECT_GT(len, 0) << LOCAL_CONTEXT;
   } else {
     EXPECT_EQ('\xCD', path[0]) << LOCAL_CONTEXT;
@@ -47,6 +50,7 @@
 
 TEST(SbSystemGetPathTest, ReturnsRequiredPaths) {
   BasicTest(kSbSystemPathContentDirectory, true, true, __LINE__);
+  BasicTest(kSbSystemPathCacheDirectory, true, true, __LINE__);
 }
 
 TEST(SbSystemGetPathTest, FailsGracefullyZeroBufferLength) {
@@ -75,12 +79,86 @@
   BasicTest(kSbSystemPathSourceDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathTempDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathTestOutputDirectory, false, false, __LINE__);
+  BasicTest(kSbSystemPathCacheDirectory, false, false, __LINE__);
 #if SB_API_VERSION >= 4
   BasicTest(kSbSystemPathFontDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathFontConfigurationDirectory, false, false, __LINE__);
 #endif  // SB_API_VERSION >= 4
 }
 
+TEST(SbSystemGetPathTest, CanCreateAndRemoveDirectoryInCache) {
+  char path[kPathSize];
+  SbMemorySet(path, 0xCD, kPathSize);
+  bool result = SbSystemGetPath(kSbSystemPathCacheDirectory, path, kPathSize);
+  EXPECT_TRUE(result);
+  if (result) {
+    EXPECT_NE('\xCD', path[0]);
+    // Delete a directory and confirm that it does not exist.
+    std::string sub_path =
+        SB_FILE_SEP_STRING + ScopedRandomFile::MakeRandomFilename();
+    EXPECT_GT(SbStringConcat(path, sub_path.c_str(), kPathSize), 0);
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+
+    // Create the directory and confirm it exists and can be opened.
+    EXPECT_TRUE(SbDirectoryCreate(path));
+    EXPECT_TRUE(SbFileExists(path));
+    EXPECT_TRUE(SbDirectoryCanOpen(path));
+    SbDirectory directory = SbDirectoryOpen(path, NULL);
+    EXPECT_TRUE(SbDirectoryIsValid(directory));
+
+    // Lastly, close and delete the directory.
+    EXPECT_TRUE(SbDirectoryClose(directory));
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+  }
+}
+TEST(SbSystemGetPathTest, CanWriteAndReadCache) {
+  char path[kPathSize];
+  SbMemorySet(path, 0xCD, kPathSize);
+  bool result = SbSystemGetPath(kSbSystemPathCacheDirectory, path, kPathSize);
+  EXPECT_TRUE(result);
+  if (result) {
+    EXPECT_NE('\xCD', path[0]);
+    int len = static_cast<int>(SbStringGetLength(path));
+    EXPECT_GT(len, 0);
+    // Delete a file and confirm that it does not exist.
+    std::string sub_path =
+        SB_FILE_SEP_STRING + ScopedRandomFile::MakeRandomFilename();
+    EXPECT_GT(SbStringConcat(path, sub_path.c_str(), kPathSize), 0);
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+
+    // Write to the file and check that we can read from it.
+    std::string content_to_write = "test content";
+    {
+      starboard::ScopedFile test_file_writer(
+          path, kSbFileCreateAlways | kSbFileWrite, NULL, NULL);
+      EXPECT_GT(
+          test_file_writer.WriteAll(content_to_write.c_str(),
+                                    static_cast<int>(content_to_write.size())),
+          0);
+    }
+    EXPECT_TRUE(SbFileExists(path));
+    SbFileInfo info;
+    EXPECT_TRUE(SbFileGetPathInfo(path, &info));
+    const int kFileSize = static_cast<int>(info.size);
+    EXPECT_GT(kFileSize, 0);
+    const int kBufferLength = 16 * 1024;
+    char content_read[kBufferLength] = {0};
+    {
+      starboard::ScopedFile test_file_reader(
+          path, kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+      EXPECT_GT(test_file_reader.ReadAll(content_read, kFileSize), 0);
+    }
+    EXPECT_EQ(content_read, content_to_write);
+
+    // Lastly, delete the file.
+    EXPECT_TRUE(SbFileDelete(path));
+    EXPECT_FALSE(SbFileExists(path));
+  }
+}
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index dc0b878..76ea308 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -396,6 +396,13 @@
 // The maximum number of users that can be signed in at the same time.
 #define SB_USER_MAX_SIGNED_IN 1
 
+// The Raspberry Pi does not apparently align fields in a heap-allocated struct
+// by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
+
+// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
 // --- Timing API ------------------------------------------------------------
 
 // Whether this platform has an API to retrieve how long the current thread
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 1170664..030fec1 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -79,6 +79,7 @@
         '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
         '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
         '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
+        '<(DEPTH)/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc',
@@ -138,7 +139,6 @@
         '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
         '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
         '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
         '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
         '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
diff --git a/src/starboard/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
index 25688a5..e0cbd79 100644
--- a/src/starboard/shared/alsa/alsa_audio_sink_type.cc
+++ b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
@@ -99,6 +99,11 @@
   }
 #endif  // SB_API_VERSION >= 4
 
+  void SetVolume(double volume) SB_OVERRIDE {
+    ScopedLock lock(mutex_);
+    volume_ = volume;
+  }
+
   bool is_valid() { return playback_handle_ != NULL; }
 
  private:
@@ -124,6 +129,7 @@
   void* context_;
 
   double playback_rate_;
+  double volume_;
   std::vector<uint8_t> resample_buffer_;
 
   int channels_;
@@ -157,6 +163,7 @@
     void* context)
     : type_(type),
       playback_rate_(1.0),
+      volume_(1.0),
       resample_buffer_(channels * kFramesPerRequest *
                        GetSampleSize(sample_type)),
       channels_(channels),
@@ -272,6 +279,7 @@
 bool AlsaAudioSink::PlaybackLoop() {
   SB_DLOG(INFO) << "alsa::AlsaAudioSink enters playback loop";
 
+  // TODO: Also handle |volume_| here.
   double playback_rate = 1.0;
   for (;;) {
     int delayed_frame = AlsaGetBufferedFrames(playback_handle_);
diff --git a/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc b/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc
new file mode 100644
index 0000000..0fe2662
--- /dev/null
+++ b/src/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/memory.h"
+
+#include "starboard/shared/dlmalloc/page_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns the number of bytes obtained from the system.  The total
+// number of bytes allocated by malloc, realloc etc., is less than this
+// value. Unlike mallinfo, this function returns only a precomputed
+// result, so can be called frequently to monitor memory consumption.
+// Even if locks are otherwise defined, this function does not use them,
+// so results might not be up to date.
+//
+// See http://gee.cs.oswego.edu/pub/misc/malloc.h for more details.
+size_t SB_ALLOCATOR(malloc_footprint)();
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+int64_t SbSystemGetUsedCPUMemory() {
+  return static_cast<int64_t>(SB_ALLOCATOR(malloc_footprint)());
+}
diff --git a/src/starboard/shared/linux/dev_input/dev_input.cc b/src/starboard/shared/linux/dev_input/dev_input.cc
index bc3202e..d0bd7fe 100644
--- a/src/starboard/shared/linux/dev_input/dev_input.cc
+++ b/src/starboard/shared/linux/dev_input/dev_input.cc
@@ -24,6 +24,8 @@
 #include <vector>
 
 #include <algorithm>
+#include <cmath>
+#include <map>
 #include <string>
 
 #include "starboard/configuration.h"
@@ -44,15 +46,44 @@
 using ::starboard::shared::starboard::Application;
 
 typedef int FileDescriptor;
-const FileDescriptor kInvalidFd = -1;
-const int kKeyboardDeviceId = 1;
+const FileDescriptor kInvalidFd = -ENODEV;
+
+enum InputDeviceIds {
+  kKeyboardDeviceId = 1,
+  kGamepadDeviceId,
+};
+
+enum TouchPadPositionState {
+  kTouchPadPositionNone = 0,
+  kTouchPadPositionX = 1,
+  kTouchPadPositionY = 2,
+  kTouchPadPositionAll = kTouchPadPositionX | kTouchPadPositionY
+};
+
+struct InputDeviceInfo {
+  InputDeviceInfo() : fd(-1), touchpad_position_state(kTouchPadPositionNone) {}
+
+  // File descriptor open for the device
+  FileDescriptor fd;
+  // Absolute Axis info.
+  std::map<int, struct input_absinfo> axis_info;
+  std::map<int, float> axis_value;
+  int touchpad_position_state;
+};
+
+bool IsTouchpadPositionKnown(InputDeviceInfo* device_info) {
+  return device_info->touchpad_position_state == kTouchPadPositionAll;
+}
 
 // Private implementation of DevInput.
 class DevInputImpl : public DevInput {
  public:
   explicit DevInputImpl(SbWindow window);
+  DevInputImpl(SbWindow window, FileDescriptor wake_up_fd);
   ~DevInputImpl() SB_OVERRIDE;
 
+  void InitDevInputImpl(SbWindow window);
+
   Event* PollNextSystemEvent() SB_OVERRIDE;
   Event* WaitForSystemEventWithTimeout(SbTime time) SB_OVERRIDE;
   void WakeSystemEventWait() SB_OVERRIDE;
@@ -61,13 +92,27 @@
   // Converts an input_event into a kSbEventInput Application::Event. The caller
   // is responsible for deleting the returned event.
   Event* InputToApplicationEvent(const struct input_event& event,
+                                 InputDeviceInfo* device_info,
                                  int modifiers);
 
+  // Converts an input_event containing a key input into a kSbEventInput
+  // Application::Event. The caller is responsible for deleting the returned
+  // event.
+  Event* KeyInputToApplicationEvent(const struct input_event& event,
+                                    int modifiers);
+
+  // Converts an input_event containing an axis event into a kSbEventInput
+  // Application::Event. The caller is responsible for deleting the returned
+  // event.
+  Event* AxisInputToApplicationEvent(const struct input_event& event,
+                                     InputDeviceInfo* device_info,
+                                     int modifiers);
+
   // The window to attribute /dev/input events to.
   SbWindow window_;
 
   // A set of read-only file descriptor of keyboard input devices.
-  std::vector<FileDescriptor> keyboard_fds_;
+  std::vector<InputDeviceInfo> input_devices_;
 
   // A file descriptor of the write end of a pipe that can be written to from
   // any thread to wake up this waiter in a thread-safe manner.
@@ -76,6 +121,8 @@
   // A file descriptor of the read end of a pipe that this waiter will wait on
   // to allow it to be signalled safely from other threads.
   FileDescriptor wakeup_read_fd_;
+
+  FileDescriptor wake_up_fd_;
 };
 
 // Helper class to manage a file descriptor set.
@@ -126,16 +173,12 @@
     case KEY_PAGEDOWN:
       return kSbKeyNext;
     case KEY_LEFT:
-    case BTN_DPAD_LEFT:
       return kSbKeyLeft;
     case KEY_RIGHT:
-    case BTN_DPAD_RIGHT:
       return kSbKeyRight;
     case KEY_DOWN:
-    case BTN_DPAD_DOWN:
       return kSbKeyDown;
     case KEY_UP:
-    case BTN_DPAD_UP:
       return kSbKeyUp;
     case KEY_ESC:
       return kSbKeyEscape;
@@ -374,6 +417,49 @@
       return kSbKeyBrightnessDown;
     case KEY_BRIGHTNESSUP:
       return kSbKeyBrightnessUp;
+
+    // Gamepad buttons.
+    //   https://www.kernel.org/doc/Documentation/input/gamepad.txt
+    case BTN_TL:
+      return kSbKeyGamepadLeftTrigger;
+    case BTN_TR:
+      return kSbKeyGamepadRightTrigger;
+    case BTN_DPAD_DOWN:
+      return kSbKeyGamepadDPadDown;
+    case BTN_DPAD_UP:
+      return kSbKeyGamepadDPadUp;
+    case BTN_DPAD_LEFT:
+      return kSbKeyGamepadDPadLeft;
+    case BTN_DPAD_RIGHT:
+      return kSbKeyGamepadDPadRight;
+    // The mapping for the buttons below can vary from controller to controller.
+    // TODO: Include button mapping for controllers with different layout.
+    case BTN_B:
+      return kSbKeyGamepad1;
+    case BTN_C:
+      return kSbKeyGamepad2;
+    case BTN_A:
+      return kSbKeyGamepad3;
+    case BTN_X:
+      return kSbKeyGamepad4;
+    case BTN_Y:
+      return kSbKeyGamepadLeftBumper;
+    case BTN_Z:
+      return kSbKeyGamepadRightBumper;
+    case BTN_TL2:
+      return kSbKeyGamepad5;
+    case BTN_TR2:
+      return kSbKeyGamepad6;
+    case BTN_SELECT:
+      return kSbKeyGamepadLeftStick;
+    case BTN_START:
+      return kSbKeyGamepadRightStick;
+    case BTN_MODE:
+      return kSbKeyGamepadSystem;
+    case BTN_THUMBL:
+      return kSbKeyGamepad1;
+    case BTN_THUMBR:
+      return kSbKeyGamepad1;
   }
   SB_DLOG(WARNING) << "Unknown code: 0x" << std::hex << code;
   return kSbKeyUnknown;
@@ -408,21 +494,129 @@
   return !!(bitset.at(bit / 8) & (1 << (bit % 8)));
 }
 
-// Searches for the keyboard /dev/input devices, opens them and returns the file
-// descriptors that report keyboard events.
-std::vector<FileDescriptor> GetKeyboardFds() {
-  const char kDevicePath[] = "/dev/input";
-  SbDirectory directory = SbDirectoryOpen(kDevicePath, NULL);
-  std::vector<FileDescriptor> fds;
-  if (!SbDirectoryIsValid(directory)) {
-    SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support, "
-                   << "unable to open: " << kDevicePath;
-    return fds;
+bool IsAxisFlat(float median, const struct input_absinfo& axis_info) {
+  SB_DCHECK((axis_info.flat * 2) <= (axis_info.maximum - axis_info.minimum));
+  return (axis_info.flat != 0) && (axis_info.value > median - axis_info.flat) &&
+         (axis_info.value < median + axis_info.flat);
+}
+
+float GetAxisValue(const struct input_absinfo& axis_info) {
+  float median =
+      static_cast<float>(axis_info.maximum + axis_info.minimum) / 2.0f;
+  if (IsAxisFlat(median, axis_info))
+    return 0;
+  float range = static_cast<float>(axis_info.maximum - axis_info.minimum);
+  float radius = range / 2.0f;
+  // Scale the axis value to [-1, 1].
+  float axis_value = (static_cast<float>(axis_info.value) - median) / radius;
+
+  if (axis_info.flat != 0) {
+    // Calculate the flat value scaled to [0, 1].
+    float flat = static_cast<float>(axis_info.flat) / range;
+
+    int sign = axis_value < 0.0f ? -1 : 1;
+    // Rescale the range:
+    // [-1.0f, -flat] to [-1.0f, 0.0f] and [flat, 1.0f] to [0.0f, 1.0f].
+    axis_value = (axis_value - sign * flat) / (1 - flat);
+  }
+  return axis_value;
+}
+
+void GetInputDeviceAbsoluteAxisInfo(int axis,
+                                    const std::vector<uint8_t>& bits,
+                                    InputDeviceInfo* info) {
+  if (IsBitSet(bits, axis)) {
+    struct input_absinfo axis_info;
+    int result = ioctl(info->fd, EVIOCGABS(axis), &axis_info);
+    if (result < 0) {
+      return;
+    }
+    info->axis_info.insert(std::make_pair(axis, axis_info));
+    info->axis_value.insert(std::make_pair(axis, GetAxisValue(axis_info)));
+  }
+}
+
+void GetInputDeviceInfo(InputDeviceInfo* info) {
+  std::vector<uint8_t> axis_bits(BytesNeededForBitSet(KEY_MAX));
+  int result =
+      ioctl(info->fd, EVIOCGBIT(EV_ABS, axis_bits.size()), axis_bits.data());
+  if (result < 0) {
+    return;
+  }
+
+  GetInputDeviceAbsoluteAxisInfo(ABS_X, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_Y, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_Z, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_RZ, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_RX, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_RY, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_HAT0X, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_HAT0Y, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_MT_POSITION_X, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_MT_POSITION_Y, axis_bits, info);
+  GetInputDeviceAbsoluteAxisInfo(ABS_MT_TRACKING_ID, axis_bits, info);
+  // TODO: Handle multi-touch using ABS_MT_SLOT.
+}
+
+FileDescriptor OpenDeviceIfKeyboardOrGamepad(const char* path) {
+  FileDescriptor fd = open(path, O_RDONLY | O_NONBLOCK);
+  if (fd < 0) {
+    // Open can fail if the application doesn't have permission to access
+    // the input device directly.
+    return kInvalidFd;
   }
 
   std::vector<uint8_t> ev_bits(BytesNeededForBitSet(EV_CNT));
   std::vector<uint8_t> key_bits(BytesNeededForBitSet(KEY_MAX));
 
+  int result = ioctl(fd, EVIOCGBIT(0, ev_bits.size()), ev_bits.data());
+  if (result < 0) {
+    close(fd);
+    return kInvalidFd;
+  }
+
+  bool has_ev_key = IsBitSet(ev_bits, EV_KEY);
+  if (!has_ev_key) {
+    close(fd);
+    return kInvalidFd;
+  }
+
+  result = ioctl(fd, EVIOCGBIT(EV_KEY, key_bits.size()), key_bits.data());
+  if (result < 0) {
+    close(fd);
+    return kInvalidFd;
+  }
+
+  bool has_key_space = IsBitSet(key_bits, KEY_SPACE);
+  bool has_gamepad_button = IsBitSet(key_bits, BTN_GAMEPAD);
+  if (!has_key_space && !has_gamepad_button) {
+    // If it doesn't have a space key or gamepad button, it may be a mouse.
+    close(fd);
+    return kInvalidFd;
+  }
+
+  result = ioctl(fd, EVIOCGRAB, 1);
+  if (result != 0) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": "
+                   << "Unable to get exclusive access to \"" << path << "\".";
+    close(fd);
+    return kInvalidFd;
+  }
+  return fd;
+}
+
+// Searches for the keyboard and game controller /dev/input devices, opens them
+// and returns the device info with a file descriptor and absolute axis details.
+std::vector<InputDeviceInfo> GetInputDevices() {
+  const char kDevicePath[] = "/dev/input";
+  SbDirectory directory = SbDirectoryOpen(kDevicePath, NULL);
+  std::vector<InputDeviceInfo> input_devices;
+  if (!SbDirectoryIsValid(directory)) {
+    SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support, "
+                   << "unable to open: " << kDevicePath;
+    return input_devices;
+  }
+
   while (true) {
     SbDirectoryEntry entry;
     if (!SbDirectoryGetNext(directory, &entry)) {
@@ -438,60 +632,25 @@
       continue;
     }
 
-    FileDescriptor fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
-    if (fd < 0) {
-      SB_DLOG(ERROR) << __FUNCTION__ << ": Unable to open \"" << path << "\".";
+    FileDescriptor fd = OpenDeviceIfKeyboardOrGamepad(path.c_str());
+    if (fd == kInvalidFd) {
       continue;
     }
+    InputDeviceInfo info;
+    info.fd = fd;
+    GetInputDeviceInfo(&info);
 
-    int result = ioctl(fd, EVIOCGBIT(0, ev_bits.size()), ev_bits.data());
-
-    if (result < 0) {
-      close(fd);
-      continue;
-    }
-
-    bool has_ev_key = IsBitSet(ev_bits, EV_KEY);
-
-    if (!has_ev_key) {
-      close(fd);
-      continue;
-    }
-
-    result = ioctl(fd, EVIOCGBIT(EV_KEY, key_bits.size()), key_bits.data());
-
-    if (result < 0) {
-      close(fd);
-      continue;
-    }
-
-    bool has_key_space = IsBitSet(key_bits, KEY_SPACE);
-
-    if (!has_key_space) {
-      // If it doesn't have a space key, it may be a mouse
-      close(fd);
-      continue;
-    }
-
-    result = ioctl(fd, EVIOCGRAB, 1);
-    if (result != 0) {
-      SB_DLOG(ERROR) << __FUNCTION__ << ": "
-                     << "Unable to get exclusive access to \"" << path << "\".";
-      close(fd);
-      continue;
-    }
-
-    SB_DCHECK(fd != kInvalidFd);
-    fds.push_back(fd);
+    SB_DCHECK(info.fd != kInvalidFd);
+    input_devices.push_back(info);
   }
 
-  if (fds.empty()) {
+  if (input_devices.empty()) {
     SB_DLOG(ERROR) << __FUNCTION__ << ": No /dev/input support. "
-                   << "No keyboards available.";
+                   << "No keyboards or game controllers available.";
   }
 
   SbDirectoryClose(directory);
-  return fds;
+  return input_devices;
 }
 
 // Returns whether |key_code|'s bit is set in the bitmap |map|, assuming
@@ -513,14 +672,14 @@
   return 0;
 }
 
-// Polls the given keyboard file descriptor for an input_event. If there are no
+// Polls the given input file descriptor for an input_event. If there are no
 // bytes available, assumes that there is no input event to read. If it gets a
 // partial event, it will assume that it will be completed, and spins until it
 // receives an entire event.
-bool PollKeyboardEvent(FileDescriptor fd,
-                       struct input_event* out_event,
-                       int* out_modifiers) {
-  if (fd == kInvalidFd) {
+bool PollInputEvent(InputDeviceInfo* device_info,
+                    struct input_event* out_event,
+                    int* out_modifiers) {
+  if (device_info->fd == kInvalidFd) {
     return false;
   }
 
@@ -531,7 +690,7 @@
   size_t remaining = kEventSize;
   char* buffer = reinterpret_cast<char*>(out_event);
   while (remaining > 0) {
-    int bytes_read = read(fd, buffer, remaining);
+    int bytes_read = read(device_info->fd, buffer, remaining);
     if (bytes_read <= 0) {
       if (errno == EAGAIN || bytes_read == 0) {
         if (remaining == kEventSize) {
@@ -544,18 +703,18 @@
       }
 
       // Some unexpected type of read error occured.
-      SB_DLOG(ERROR) << __FUNCTION__ << ": Error reading keyboard: " << errno
+      SB_DLOG(ERROR) << __FUNCTION__ << ": Error reading input: " << errno
                      << " - " << strerror(errno);
       return false;
     }
 
-    SB_DCHECK(bytes_read <= remaining) << "bytes_read=" << bytes_read
-                                       << ", remaining=" << remaining;
+    SB_DCHECK(bytes_read <= remaining)
+        << "bytes_read=" << bytes_read << ", remaining=" << remaining;
     remaining -= bytes_read;
     buffer += bytes_read;
   }
 
-  if (out_event->type != EV_KEY) {
+  if ((out_event->type != EV_KEY) && (out_event->type != EV_ABS)) {
     return false;
   }
 
@@ -563,7 +722,7 @@
   int modifiers = 0;
   char map[(KEY_MAX / 8) + 1] = {0};
   errno = 0;
-  int result = ioctl(fd, EVIOCGKEY(sizeof(map)), map);
+  int result = ioctl(device_info->fd, EVIOCGKEY(sizeof(map)), map);
   if (result != -1) {
     modifiers |=
         GetModifier(KEY_LEFTSHIFT, KEY_RIGHTSHIFT, kSbKeyModifiersShift, map);
@@ -593,20 +752,35 @@
 
 // Also in starboard/shared/libevent/socket_waiter_internal.cc
 // TODO: Consider consolidating.
-int SetNonBlocking(int fd) {
+int SetNonBlocking(FileDescriptor fd) {
   int flags = fcntl(fd, F_GETFL, 0);
-  if (flags == -1)
+  if (flags == -1) {
     flags = 0;
+  }
   return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 }
 
 DevInputImpl::DevInputImpl(SbWindow window)
     : window_(window),
-      keyboard_fds_(GetKeyboardFds()),
+      input_devices_(GetInputDevices()),
       wakeup_write_fd_(kInvalidFd),
-      wakeup_read_fd_(kInvalidFd) {
+      wakeup_read_fd_(kInvalidFd),
+      wake_up_fd_(kInvalidFd) {
+  InitDevInputImpl(window);
+}
+
+DevInputImpl::DevInputImpl(SbWindow window, FileDescriptor wake_up_fd)
+    : window_(window),
+      input_devices_(GetInputDevices()),
+      wakeup_write_fd_(kInvalidFd),
+      wakeup_read_fd_(kInvalidFd),
+      wake_up_fd_(wake_up_fd) {
+  InitDevInputImpl(window);
+}
+
+void DevInputImpl::InitDevInputImpl(SbWindow window) {
   // Initialize wakeup pipe.
-  int fds[2] = {kInvalidFd, kInvalidFd};
+  FileDescriptor fds[2] = {kInvalidFd, kInvalidFd};
   int result = pipe(fds);
   SB_DCHECK(result == 0) << "result=" << result;
 
@@ -622,9 +796,8 @@
 }
 
 DevInputImpl::~DevInputImpl() {
-  for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin();
-       it != keyboard_fds_.end(); ++it) {
-    close(*it);
+  for (const auto& device : input_devices_) {
+    close(device.fd);
   }
   CloseFdSafely(&wakeup_write_fd_);
   CloseFdSafely(&wakeup_read_fd_);
@@ -633,13 +806,12 @@
 DevInput::Event* DevInputImpl::PollNextSystemEvent() {
   struct input_event event;
   int modifiers = 0;
-  for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin();
-       it != keyboard_fds_.end(); ++it) {
-    if (!PollKeyboardEvent(*it, &event, &modifiers)) {
+  for (auto& device : input_devices_) {
+    if (!PollInputEvent(&device, &event, &modifiers)) {
       continue;
     }
 
-    return InputToApplicationEvent(event, modifiers);
+    return InputToApplicationEvent(event, &device, modifiers);
   }
   return NULL;
 }
@@ -651,9 +823,13 @@
   }
 
   FdSet read_set;
-  for (std::vector<FileDescriptor>::const_iterator it = keyboard_fds_.begin();
-       it != keyboard_fds_.end(); ++it) {
-    read_set.Set(*it);
+  if (wake_up_fd_ != kInvalidFd) {
+    read_set.Set(wake_up_fd_);
+  }
+
+  for (std::vector<InputDeviceInfo>::const_iterator it = input_devices_.begin();
+       it != input_devices_.end(); ++it) {
+    read_set.Set(it->fd);
   }
   read_set.Set(wakeup_read_fd_);
 
@@ -693,13 +869,256 @@
   }
 }
 
-DevInput::Event* DevInputImpl::InputToApplicationEvent(
-    const struct input_event& event,
-    int modifiers) {
-  if (event.type != EV_KEY) {
+namespace {
+
+// Creates a key event for an analog button input.
+DevInput::Event* CreateAnalogButtonKeyEvent(SbWindow window,
+                                            float axis_value,
+                                            float previous_axis_value,
+                                            SbKey key,
+                                            SbKeyLocation location,
+                                            int modifiers,
+                                            const struct input_event& event) {
+  SbInputEventType previous_type =
+      (std::abs(previous_axis_value) > 0.5 ? kSbInputEventTypePress
+                                           : kSbInputEventTypeUnpress);
+  SbInputEventType type =
+      (std::abs(axis_value) > 0.5 ? kSbInputEventTypePress
+                                  : kSbInputEventTypeUnpress);
+  if (previous_type == type) {
+    // Key press/unpress state did not change.
     return NULL;
   }
 
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+  data->window = window;
+  data->type = type;
+  data->device_type = kSbInputDeviceTypeGamepad;
+  data->device_id = kGamepadDeviceId;
+  data->key = key;
+  data->key_location = location;
+  data->key_modifiers = modifiers;
+  return new DevInput::Event(kSbEventTypeInput, data,
+                             &Application::DeleteDestructor<SbInputData>);
+}
+
+// Creates a move event with key for a stick input.
+DevInput::Event* CreateMoveEventWithKey(SbWindow window,
+                                        SbKey key,
+                                        SbKeyLocation location,
+                                        int modifiers,
+                                        const SbInputVector& input_vector) {
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+
+  data->window = window;
+  data->type = kSbInputEventTypeMove;
+  data->device_type = kSbInputDeviceTypeGamepad;
+  data->device_id = kGamepadDeviceId;
+
+  data->key = key;
+  data->key_location = location;
+  data->key_modifiers = modifiers;
+  data->position = input_vector;
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+  data->pressure = NAN;
+  data->size = {NAN, NAN};
+  data->tilt = {NAN, NAN};
+#endif
+
+  return new DevInput::Event(kSbEventTypeInput, data,
+                             &Application::DeleteDestructor<SbInputData>);
+}
+
+DevInput::Event* CreateTouchPadEvent(SbWindow window,
+                                     SbInputEventType type,
+                                     SbKey key,
+                                     SbKeyLocation location,
+                                     int modifiers,
+                                     const SbInputVector& input_vector) {
+  SbInputData* data = new SbInputData();
+  SbMemorySet(data, 0, sizeof(*data));
+
+  data->window = window;
+  data->type = type;
+  data->device_type = kSbInputDeviceTypeTouchPad;
+  data->device_id = kGamepadDeviceId;
+
+  data->key = key;
+  data->key_location = location;
+  data->key_modifiers = modifiers;
+  data->position = input_vector;
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+  data->pressure = NAN;
+  data->size = {NAN, NAN};
+  data->tilt = {NAN, NAN};
+#endif
+
+  return new DevInput::Event(kSbEventTypeInput, data,
+                             &Application::DeleteDestructor<SbInputData>);
+}
+
+}  // namespace
+
+DevInput::Event* DevInputImpl::AxisInputToApplicationEvent(
+    const struct input_event& event,
+    InputDeviceInfo* device_info,
+    int modifiers) {
+  SB_DCHECK(event.type == EV_ABS);
+  SbKey key = kSbKeyUnknown;
+  float axis_value = 0;
+  float previous_axis_value = 0;
+  auto axis_info_it = device_info->axis_info.find(event.code);
+  if (axis_info_it != device_info->axis_info.end()) {
+    struct input_absinfo& axis_info = axis_info_it->second;
+    axis_info.value = event.value;
+    axis_value = GetAxisValue(axis_info);
+    float& stored_axis_value = device_info->axis_value[event.code];
+    previous_axis_value = stored_axis_value;
+    if (previous_axis_value == axis_value) {
+      // If the value is unchanged, don't do anything.
+      return NULL;
+    }
+    stored_axis_value = axis_value;
+  }
+
+  SbKeyLocation location = kSbKeyLocationUnspecified;
+  SbInputVector input_vector;
+  // The mapping for the axis codes can vary from controller to controller.
+  // TODO: Include axis mapping for controllers with different layout.
+  switch (event.code) {
+    case ABS_X:
+      // Report up and left as positive values.
+      input_vector.x = -axis_value;
+      input_vector.y = -device_info->axis_value[ABS_Y];
+      key = kSbKeyGamepadLeftStickLeft;
+      location = kSbKeyLocationLeft;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    case ABS_Y: {
+      // Report up and left as positive values.
+      input_vector.x = -device_info->axis_value[ABS_X];
+      input_vector.y = -axis_value;
+      key = kSbKeyGamepadLeftStickUp;
+      location = kSbKeyLocationLeft;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    }
+    case ABS_Z:
+      // Report up and left as positive values.
+      input_vector.x = -axis_value;
+      input_vector.y = -device_info->axis_value[ABS_RZ];
+      key = kSbKeyGamepadRightStickLeft;
+      location = kSbKeyLocationRight;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    case ABS_RZ:
+      // Report up and left as positive values.
+      input_vector.x = -device_info->axis_value[ABS_Z];
+      input_vector.y = -axis_value;
+      key = kSbKeyGamepadRightStickUp;
+      location = kSbKeyLocationRight;
+      return CreateMoveEventWithKey(window_, key, location, modifiers,
+                                    input_vector);
+    case ABS_RX: {
+      key = kSbKeyGamepadLeftTrigger;
+      location = kSbKeyLocationLeft;
+      // For trigger buttons, the range is [0..1].
+      float trigger_value = (axis_value + 1) / 2;
+      float previous_trigger_value = (previous_axis_value + 1) / 2;
+      return CreateAnalogButtonKeyEvent(window_, trigger_value,
+                                        previous_trigger_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_RY: {
+      key = kSbKeyGamepadRightTrigger;
+      location = kSbKeyLocationRight;
+      // For trigger buttons, the range is [0..1].
+      float trigger_value = (axis_value + 1) / 2;
+      float previous_trigger_value = (previous_axis_value + 1) / 2;
+      return CreateAnalogButtonKeyEvent(window_, trigger_value,
+                                        previous_trigger_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_HAT0X: {
+      float axis_value_for_key =
+          std::abs(axis_value) > 0.5f ? axis_value : previous_axis_value;
+      key = (axis_value_for_key < 0) ? kSbKeyGamepadDPadLeft
+                                     : kSbKeyGamepadDPadRight;
+      return CreateAnalogButtonKeyEvent(window_, axis_value,
+                                        previous_axis_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_HAT0Y: {
+      float axis_value_for_key =
+          std::abs(axis_value) > 0.5f ? axis_value : previous_axis_value;
+      key = (axis_value_for_key < 0) ? kSbKeyGamepadDPadUp
+                                     : kSbKeyGamepadDPadDown;
+      return CreateAnalogButtonKeyEvent(window_, axis_value,
+                                        previous_axis_value, key, location,
+                                        modifiers, event);
+    }
+    case ABS_MT_TRACKING_ID:
+      if (event.value == -1) {
+        bool touchpad_position_is_known = IsTouchpadPositionKnown(device_info);
+        device_info->touchpad_position_state = kTouchPadPositionNone;
+        if (touchpad_position_is_known) {
+          // Touch point is released, report last known position as unpress.
+          input_vector.x = (device_info->axis_value[ABS_MT_POSITION_X] + 1 / 2);
+          input_vector.y = (device_info->axis_value[ABS_MT_POSITION_Y] + 1 / 2);
+          return CreateTouchPadEvent(window_, kSbInputEventTypeUnpress, key,
+                                     location, modifiers, input_vector);
+        }
+      }
+      return NULL;
+    case ABS_MT_POSITION_X: {
+      // If all positions were known before this event, then this event is a
+      // move.
+      SbInputEventType type = IsTouchpadPositionKnown(device_info)
+                                  ? kSbInputEventTypeMove
+                                  : kSbInputEventTypePress;
+      device_info->touchpad_position_state |= kTouchPadPositionX;
+      if (IsTouchpadPositionKnown(device_info)) {
+        // For touchpads, the range is [0..1].
+        input_vector.x = (axis_value + 1) / 2;
+        input_vector.y = (device_info->axis_value[ABS_MT_POSITION_Y] + 1) / 2;
+        return CreateTouchPadEvent(window_, type, key, location, modifiers,
+                                   input_vector);
+      }
+      // Not all axis positions are known yet.
+      return NULL;
+    }
+    case ABS_MT_POSITION_Y: {
+      // If all positions were known before this event, then this event is a
+      // move.
+      SbInputEventType type = IsTouchpadPositionKnown(device_info)
+                                  ? kSbInputEventTypeMove
+                                  : kSbInputEventTypePress;
+      device_info->touchpad_position_state |= kTouchPadPositionY;
+      if (IsTouchpadPositionKnown(device_info)) {
+        // For touchpads, the range is [0..1].
+        input_vector.x = (device_info->axis_value[ABS_MT_POSITION_X] + 1) / 2;
+        input_vector.y = (axis_value + 1) / 2;
+        return CreateTouchPadEvent(window_, type, key, location, modifiers,
+                                   input_vector);
+      }
+      // Not all axis positions are known yet.
+      return NULL;
+    }
+    default:
+      // Ignored event codes.
+      return NULL;
+  }
+
+  SB_NOTREACHED();
+  return NULL;
+}
+
+DevInput::Event* DevInputImpl::KeyInputToApplicationEvent(
+    const struct input_event& event,
+    int modifiers) {
+  SB_DCHECK(event.type == EV_KEY);
   SB_DCHECK(event.value <= 2);
   SbInputData* data = new SbInputData();
   SbMemorySet(data, 0, sizeof(*data));
@@ -715,6 +1134,21 @@
                    &Application::DeleteDestructor<SbInputData>);
 }
 
+DevInput::Event* DevInputImpl::InputToApplicationEvent(
+    const struct input_event& event,
+    InputDeviceInfo* device_info,
+    int modifiers) {
+  // EV_ABS events are axis values: Sticks, dpad, and touchpad.
+  // https://www.kernel.org/doc/Documentation/input/event-codes.txt
+  switch (event.type) {
+    case EV_ABS:
+      return AxisInputToApplicationEvent(event, device_info, modifiers);
+    case EV_KEY:
+      return KeyInputToApplicationEvent(event, modifiers);
+  }
+  return NULL;
+}
+
 }  // namespace
 
 // static
@@ -722,6 +1156,11 @@
   return new DevInputImpl(window);
 }
 
+// static
+DevInput* DevInput::Create(SbWindow window, int wake_up_fd) {
+  return new DevInputImpl(window, wake_up_fd);
+}
+
 }  // namespace dev_input
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/linux/dev_input/dev_input.h b/src/starboard/shared/linux/dev_input/dev_input.h
index aa8577a..11b94c4 100644
--- a/src/starboard/shared/linux/dev_input/dev_input.h
+++ b/src/starboard/shared/linux/dev_input/dev_input.h
@@ -48,6 +48,11 @@
   // Creates an instance of DevInput for the given window.
   static DevInput* Create(SbWindow window);
 
+  // Creates an instance of DevInput for the given window.
+  // The wake_up_fd will be used in WaitForSystemEventWithTimeout() to return
+  // early when input is available on it.
+  static DevInput* Create(SbWindow window, int wake_up_fd);
+
  protected:
   DevInput() {}
 };
diff --git a/src/starboard/shared/msvc/uwp/toolchain.py b/src/starboard/shared/msvc/uwp/msvc_toolchain.py
similarity index 100%
rename from src/starboard/shared/msvc/uwp/toolchain.py
rename to src/starboard/shared/msvc/uwp/msvc_toolchain.py
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
index cf40ad5..4aaeef1 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
@@ -42,6 +42,9 @@
 #if SB_API_VERSION >= 4
   virtual void SetPlaybackRate(double playback_rate) = 0;
 #endif  // SB_API_VERSION >= 4
+
+  virtual void SetVolume(double volume) = 0;
+
   virtual bool IsType(Type* type) = 0;
 
   // The following two functions will be called during application startup and
diff --git a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
index 597820a..f6ccd09 100644
--- a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
+++ b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
@@ -44,6 +44,10 @@
   }
 #endif  // SB_API_VERSION >= 4
 
+  void SetVolume(double volume) SB_OVERRIDE {
+    SB_UNREFERENCED_PARAMETER(volume);
+  }
+
  private:
   static void* ThreadEntryPoint(void* context);
   void AudioThreadFunc();
diff --git a/src/starboard/shared/starboard/file_storage/storage_delete_record.cc b/src/starboard/shared/starboard/file_storage/storage_delete_record.cc
index 891fbbb..c5c7fe3 100644
--- a/src/starboard/shared/starboard/file_storage/storage_delete_record.cc
+++ b/src/starboard/shared/starboard/file_storage/storage_delete_record.cc
@@ -18,14 +18,23 @@
 #include "starboard/shared/starboard/file_storage/storage_internal.h"
 #include "starboard/user.h"
 
-bool SbStorageDeleteRecord(SbUser user) {
+bool SbStorageDeleteRecord(SbUser user
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ,
+                           const char* name
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ) {
   if (!SbUserIsValid(user)) {
     return false;
   }
 
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+  const char* name = NULL;
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
   char path[SB_FILE_MAX_PATH];
   bool success = starboard::shared::starboard::GetUserStorageFilePath(
-      user, path, SB_ARRAY_SIZE_INT(path));
+      user, name, path, SB_ARRAY_SIZE_INT(path));
   if (!success) {
     return false;
   }
diff --git a/src/starboard/shared/starboard/file_storage/storage_internal.h b/src/starboard/shared/starboard/file_storage/storage_internal.h
index 6bb3362..a56cd34 100644
--- a/src/starboard/shared/starboard/file_storage/storage_internal.h
+++ b/src/starboard/shared/starboard/file_storage/storage_internal.h
@@ -18,6 +18,8 @@
 #ifndef STARBOARD_SHARED_STARBOARD_FILE_STORAGE_STORAGE_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_FILE_STORAGE_STORAGE_INTERNAL_H_
 
+#include <string>
+
 #include "starboard/file.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/storage.h"
@@ -27,6 +29,9 @@
 struct SbStorageRecordPrivate {
   SbUser user;
   SbFile file;
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  std::string name;
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
 };
 
 namespace starboard {
@@ -34,6 +39,7 @@
 namespace starboard {
 // Gets the path to the storage file for the given user.
 static SB_C_INLINE bool GetUserStorageFilePath(SbUser user,
+                                               const char* name,
                                                char* out_path,
                                                int path_size) {
   bool success = SbUserGetProperty(user, kSbUserPropertyHomeDirectory, out_path,
@@ -42,7 +48,12 @@
     return false;
   }
 
-  SbStringConcat(out_path, "/.starboard.storage", path_size);
+  SbStringConcat(out_path, "/.starboard", path_size);
+  if (name && SbStringGetLength(name) > 0) {
+    SbStringConcat(out_path, ".", path_size);
+    SbStringConcat(out_path, name, path_size);
+  }
+  SbStringConcat(out_path, ".storage", path_size);
   return true;
 }
 }  // namespace starboard
diff --git a/src/starboard/shared/starboard/file_storage/storage_open_record.cc b/src/starboard/shared/starboard/file_storage/storage_open_record.cc
index 5be40f4..aaea393 100644
--- a/src/starboard/shared/starboard/file_storage/storage_open_record.cc
+++ b/src/starboard/shared/starboard/file_storage/storage_open_record.cc
@@ -19,14 +19,23 @@
 #include "starboard/shared/starboard/file_storage/storage_internal.h"
 #include "starboard/user.h"
 
-SbStorageRecord SbStorageOpenRecord(SbUser user) {
+SbStorageRecord SbStorageOpenRecord(SbUser user
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ,
+                                    const char* name
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ) {
   if (!SbUserIsValid(user)) {
     return kSbStorageInvalidRecord;
   }
 
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+  const char* name = NULL;
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
   char path[SB_FILE_MAX_PATH];
   bool success = starboard::shared::starboard::GetUserStorageFilePath(
-      user, path, SB_ARRAY_SIZE_INT(path));
+      user, name, path, SB_ARRAY_SIZE_INT(path));
   if (!success) {
     return kSbStorageInvalidRecord;
   }
@@ -43,5 +52,10 @@
   SB_DCHECK(SbStorageIsValidRecord(result));
   result->user = user;
   result->file = file;
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  if (name) {
+    result->name = name;
+  }
+#endif
   return result;
 }
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index dbaa49e..9e63967 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -71,6 +71,7 @@
       sink_sample_type_(GetSinkAudioSampleType()),
       bytes_per_frame_(media::GetBytesPerSample(sink_sample_type_) * channels_),
       playback_rate_(1.0),
+      volume_(1.0),
       paused_(true),
       seeking_(false),
       seeking_to_pts_(0),
@@ -178,6 +179,14 @@
 }
 #endif  // SB_API_VERSION >= 4
 
+void AudioRendererImpl::SetVolume(double volume) {
+  SB_DCHECK(BelongsToCurrentThread());
+  volume_ = volume;
+  if (audio_sink_) {
+    audio_sink_->SetVolume(volume_);
+  }
+}
+
 void AudioRendererImpl::Seek(SbMediaTime seek_to_pts) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(seek_to_pts >= 0);
@@ -287,6 +296,7 @@
   // support play/pause.
   audio_sink_->SetPlaybackRate(playback_rate_ > 0.0 ? 1.0 : 0.0);
 #endif  // SB_API_VERSION >= 4
+  audio_sink_->SetVolume(volume_);
 }
 
 void AudioRendererImpl::UpdateSourceStatus(int* frames_in_buffer,
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index d9278da..a469037 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -60,6 +60,7 @@
 #if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate) SB_OVERRIDE;
 #endif  // SB_API_VERSION >= 4
+  void SetVolume(double volume) SB_OVERRIDE;
   void Seek(SbMediaTime seek_to_pts) SB_OVERRIDE;
 
   bool IsEndOfStreamWritten() const SB_OVERRIDE {
@@ -117,6 +118,7 @@
   scoped_ptr<AudioResampler> resampler_;
   AudioTimeStretcher time_stretcher_;
   double playback_rate_;
+  double volume_;
 
   atomic_bool paused_;
   atomic_bool seeking_;
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 1c192c1..a6ca64b 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -38,6 +38,7 @@
 #if SB_API_VERSION >= 4
   virtual void SetPlaybackRate(double playback_rate) = 0;
 #endif  // SB_API_VERSION >= 4
+  virtual void SetVolume(double volume) = 0;
   virtual void Seek(SbMediaTime seek_to_pts) = 0;
   virtual bool IsEndOfStreamWritten() const = 0;
   virtual bool IsEndOfStreamPlayed() const = 0;
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 76b6e53..c373d90 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
@@ -62,7 +62,11 @@
       audio_codec_(audio_codec),
       drm_system_(drm_system),
       audio_header_(audio_header),
-      paused_(false)
+      paused_(false),
+#if SB_API_VERSION >= 4
+      playback_rate_(1.0),
+#endif  // SB_API_VERSION >= 4
+      volume_(1.0)
 #if SB_API_VERSION >= 4
       ,
       output_mode_(output_mode),
@@ -87,12 +91,12 @@
   bounds_ = PlayerWorker::Bounds();
 }
 
-bool FilterBasedPlayerWorkerHandler::IsPunchoutMode() const {

-#if SB_API_VERSION >= 4

-  return (output_mode_ == kSbPlayerOutputModePunchOut);

-#else

-  return true;

-#endif  // SB_API_VERSION >= 4

+bool FilterBasedPlayerWorkerHandler::IsPunchoutMode() const {
+#if SB_API_VERSION >= 4
+  return (output_mode_ == kSbPlayerOutputModePunchOut);
+#else
+  return true;
+#endif  // SB_API_VERSION >= 4
 }
 
 bool FilterBasedPlayerWorkerHandler::Init(
@@ -144,6 +148,11 @@
   ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
   media_components->GetRenderers(&audio_renderer_, &video_renderer_);
 
+#if SB_API_VERSION >= 4
+  audio_renderer_->SetPlaybackRate(playback_rate_);
+#endif  // SB_API_VERSION >= 4
+  audio_renderer_->SetVolume(volume_);
+
   job_queue_->Schedule(update_closure_, kUpdateInterval);
 
   return true;
@@ -276,15 +285,26 @@
 bool FilterBasedPlayerWorkerHandler::SetPlaybackRate(double playback_rate) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
+  playback_rate_ = playback_rate;
+
   if (!audio_renderer_) {
     return false;
   }
 
-  audio_renderer_->SetPlaybackRate(playback_rate);
+  audio_renderer_->SetPlaybackRate(playback_rate_);
   return true;
 }
 #endif  // SB_API_VERSION >= 4
 
+void FilterBasedPlayerWorkerHandler::SetVolume(double volume) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  volume_ = volume;
+  if (audio_renderer_) {
+    audio_renderer_->SetVolume(volume_);
+  }
+}
+
 bool FilterBasedPlayerWorkerHandler::SetBounds(
     const PlayerWorker::Bounds& bounds) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
@@ -328,9 +348,9 @@
     player_worker_->UpdateDroppedVideoFrames(
         video_renderer_->GetDroppedFrames());
 
-   if (IsPunchoutMode()) {

-      shared::starboard::Application::Get()->HandleFrame(

-          player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);

+    if (IsPunchoutMode()) {
+      shared::starboard::Application::Get()->HandleFrame(
+          player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
     }
 
     (*player_worker_.*update_media_time_cb_)(audio_renderer_->GetCurrentTime());
@@ -355,10 +375,10 @@
   }
   video_renderer.reset();
 
-  if (IsPunchoutMode()) {

-    // Clear the video frame as we terminate.

-    shared::starboard::Application::Get()->HandleFrame(

-        player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);

+  if (IsPunchoutMode()) {
+    // Clear the video frame as we terminate.
+    shared::starboard::Application::Get()->HandleFrame(
+        player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);
   }
 }
 
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 8715ab9..20655ca 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
@@ -68,6 +68,7 @@
 #if SB_API_VERSION >= 4
   bool SetPlaybackRate(double playback_rate) SB_OVERRIDE;
 #endif  // SB_API_VERSION >= 4
+  void SetVolume(double volume) SB_OVERRIDE;
   bool SetBounds(const PlayerWorker::Bounds& bounds) SB_OVERRIDE;
   void Stop() SB_OVERRIDE;
 
@@ -98,6 +99,10 @@
   scoped_ptr<VideoRenderer> video_renderer_;
 
   bool paused_;
+#if SB_API_VERSION >= 4
+  double playback_rate_;
+#endif  // SB_API_VERSION >= 4
+  double volume_;
   PlayerWorker::Bounds bounds_;
   Closure update_closure_;
 
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index 20268ea..3b46a5d 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -133,8 +133,8 @@
 #endif  // SB_API_VERSION >= 4
 
 void SbPlayerPrivate::SetVolume(double volume) {
-  SB_UNREFERENCED_PARAMETER(volume);
-  SB_NOTIMPLEMENTED();
+  volume_ = volume;
+  worker_->SetVolume(volume_);
 }
 
 void SbPlayerPrivate::UpdateMediaTime(SbMediaTime media_time, int ticket) {
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 38a0a25..24e834c 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -270,6 +270,11 @@
 }
 #endif  // SB_API_VERSION >= 4
 
+void PlayerWorker::DoSetVolume(double volume) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+  handler_->SetVolume(volume);
+}
+
 void PlayerWorker::DoStop() {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index a1d9333..84be839 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -83,6 +83,8 @@
 #if SB_API_VERSION >= 4
     virtual bool SetPlaybackRate(double playback_rate) = 0;
 #endif  // SB_API_VERSION >= 4
+    virtual void SetVolume(double volume) = 0;
+
     virtual bool SetBounds(const Bounds& bounds) = 0;
 
     // Once this function returns, all processing on the Handler and related
@@ -147,6 +149,10 @@
   }
 #endif  // SB_API_VERSION >= 4
 
+  void SetVolume(double volume) {
+    job_queue_->Schedule(Bind(&PlayerWorker::DoSetVolume, this, volume));
+  }
+
   void UpdateDroppedVideoFrames(int dropped_video_frames) {
     host_->UpdateDroppedVideoFrames(dropped_video_frames);
   }
@@ -177,6 +183,7 @@
 #if SB_API_VERSION >= 4
   void DoSetPlaybackRate(double rate);
 #endif  // SB_API_VERSION >= 4
+  void DoSetVolume(double volume);
   void DoStop();
 
   void UpdateDecoderState(SbMediaType type, SbPlayerDecoderState state);
diff --git a/src/starboard/shared/stub/drm_create_system.cc b/src/starboard/shared/stub/drm_create_system.cc
index a9a1c84..64139a9 100644
--- a/src/starboard/shared/stub/drm_create_system.cc
+++ b/src/starboard/shared/stub/drm_create_system.cc
@@ -14,6 +14,24 @@
 
 #include "starboard/drm.h"
 
+#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
+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;
+}
+
+#else  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
+
 SbDrmSystem SbDrmCreateSystem(
     const char* key_system,
     void* context,
@@ -25,3 +43,5 @@
   SB_UNREFERENCED_PARAMETER(session_updated_callback);
   return kSbDrmSystemInvalid;
 }
+
+#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
diff --git a/src/starboard/shared/stub/storage_delete_record.cc b/src/starboard/shared/stub/storage_delete_record.cc
index ba13cd1..a669054 100644
--- a/src/starboard/shared/stub/storage_delete_record.cc
+++ b/src/starboard/shared/stub/storage_delete_record.cc
@@ -14,6 +14,11 @@
 
 #include "starboard/storage.h"
 
-bool SbStorageDeleteRecord(SbUser /*user*/) {
+bool SbStorageDeleteRecord(SbUser /*user*/
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ,
+                           const char* /*name*/
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                           ) {
   return false;
 }
diff --git a/src/starboard/shared/stub/storage_open_record.cc b/src/starboard/shared/stub/storage_open_record.cc
index bcd5f3d..b0f503e 100644
--- a/src/starboard/shared/stub/storage_open_record.cc
+++ b/src/starboard/shared/stub/storage_open_record.cc
@@ -14,6 +14,11 @@
 
 #include "starboard/storage.h"
 
-SbStorageRecord SbStorageOpenRecord(SbUser /*user*/) {
+SbStorageRecord SbStorageOpenRecord(SbUser /*user*/
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ,
+                                    const char* /*name*/
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+                                    ) {
   return kSbStorageInvalidRecord;
 }
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/test_webapi_extension/my_new_enum.idl
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/test_webapi_extension/my_new_enum.idl
index be4e805..1a6f73c 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/test_webapi_extension/my_new_enum.idl
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+enum MyNewEnum {
+  "apples",
+  "oranges",
+  "peaches"
+};
diff --git a/src/starboard/shared/test_webapi_extension/my_new_interface.cc b/src/starboard/shared/test_webapi_extension/my_new_interface.cc
new file mode 100644
index 0000000..18af5b4
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/my_new_interface.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/test_webapi_extension/my_new_interface.h"
+
+namespace cobalt {
+namespace webapi_extension {
+
+MyNewInterface::MyNewInterface(const scoped_refptr<dom::Window>& window) {
+  UNREFERENCED_PARAMETER(window);
+  // Provide an initial value for the enum.
+  enum_value_ = kMyNewEnumApples;
+}
+
+MyNewInterface::~MyNewInterface() OVERRIDE {}
+
+}  // namespace webapi_extension
+}  // namespace cobalt
diff --git a/src/starboard/shared/test_webapi_extension/my_new_interface.h b/src/starboard/shared/test_webapi_extension/my_new_interface.h
new file mode 100644
index 0000000..eb30d4e
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/my_new_interface.h
@@ -0,0 +1,55 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
+#define STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
+
+#include <string>
+
+#include "cobalt/dom/window.h"
+#include "cobalt/script/wrappable.h"
+#include "starboard/shared/test_webapi_extension/my_new_enum.h"
+
+namespace cobalt {
+namespace webapi_extension {
+
+class MyNewInterface : public script::Wrappable {
+ public:
+  explicit MyNewInterface(const scoped_refptr<dom::Window>& window);
+
+  const std::string& foo() const { return foo_; }
+  void set_foo(const std::string& value) { foo_ = value; }
+
+  void SetMyNewEnum(MyNewEnum value) { enum_value_ = value; }
+  MyNewEnum GetMyNewEnum() const { return enum_value_; }
+
+  // All types derived from script::Wrappable must have this annotation.
+  DEFINE_WRAPPABLE_TYPE(MyNewInterface);
+
+ private:
+  // Since script::Wrappable inherits from base::RefCounted<>, we make the
+  // destructor private.
+  ~MyNewInterface() OVERRIDE;
+
+  std::string foo_;
+
+  MyNewEnum enum_value_;
+
+  DISALLOW_COPY_AND_ASSIGN(MyNewInterface);
+};
+
+}  // namespace webapi_extension
+}  // namespace cobalt
+
+#endif  // STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/test_webapi_extension/my_new_interface.idl
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/test_webapi_extension/my_new_interface.idl
index be4e805..3e64764 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/test_webapi_extension/my_new_interface.idl
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+interface MyNewInterface {
+  attribute DOMString foo;
 
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+  void SetMyNewEnum(MyNewEnum value);
+  MyNewEnum GetMyNewEnum();
+};
diff --git a/src/starboard/shared/test_webapi_extension/test_webapi_extension.gypi b/src/starboard/shared/test_webapi_extension/test_webapi_extension.gypi
new file mode 100644
index 0000000..74de31a
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/test_webapi_extension.gypi
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file can be included from a platform's gyp_configuration.gypi file to
+# setup the test custom webapi_extension defined in this directory to be
+# injected into that platform.
+{
+  'variables': {
+    'cobalt_webapi_extension_source_idl_files': [
+      'my_new_interface.idl'
+    ],
+    'cobalt_webapi_extension_generated_header_idl_files': [
+      'my_new_enum.idl'
+    ],
+    'cobalt_webapi_extension_gyp_target':
+      '<(DEPTH)/starboard/shared/test_webapi_extension/webapi_extension.gyp:cobalt_test_webapi_extension',
+  },
+}
\ No newline at end of file
diff --git a/src/starboard/shared/test_webapi_extension/webapi_extension.cc b/src/starboard/shared/test_webapi_extension/webapi_extension.cc
new file mode 100644
index 0000000..06bb89c
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/webapi_extension.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/webapi_extension.h"
+
+#include "base/compiler_specific.h"
+#include "cobalt/script/global_environment.h"
+#include "starboard/shared/test_webapi_extension/my_new_interface.h"
+
+namespace cobalt {
+namespace browser {
+
+base::optional<std::string> GetWebAPIExtensionObjectPropertyName() {
+  return std::string("myInterface");
+}
+
+scoped_refptr<script::Wrappable> CreateWebAPIExtensionObject(
+    const scoped_refptr<dom::Window>& window,
+    script::GlobalEnvironment* global_environment) {
+  UNREFERENCED_PARAMETER(global_environment);
+
+  return scoped_refptr<script::Wrappable>(
+      new webapi_extension::MyNewInterface(window));
+}
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/starboard/shared/test_webapi_extension/webapi_extension.gyp b/src/starboard/shared/test_webapi_extension/webapi_extension.gyp
new file mode 100644
index 0000000..5c238e0
--- /dev/null
+++ b/src/starboard/shared/test_webapi_extension/webapi_extension.gyp
@@ -0,0 +1,35 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'targets': [
+    {
+      'target_name': 'cobalt_test_webapi_extension',
+      'type': 'static_library',
+
+      # List of all source files and header files needed to support the IDL
+      # definitions.
+      'sources': [
+        'my_new_interface.h',
+        'my_new_interface.cc',
+        'webapi_extension.cc',
+      ],
+
+      'dependencies': [
+        '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+        '<(DEPTH)/cobalt/script/script.gyp:script',
+        '<(DEPTH)/base/base.gyp:base',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
index eaf8bd3..6f71b80 100644
--- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
+++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
@@ -98,7 +98,7 @@
 
 WebTokenRequestResult^ RequestToken(WebTokenRequest^ request) {
   using starboard::shared::uwp::WaitForResult;
-  IAsyncOperation<WebTokenRequestResult ^> ^ request_operation = nullptr;
+  IAsyncOperation<WebTokenRequestResult^>^ request_operation = nullptr;
   base::WaitableEvent request_operation_set(false, false);
   // Ensure WebAuthenticationCoreManager::RequestTokenAsync is called on the
   // UI thread, since documentation states that "This method cannot be called
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
index ee1b5bd..85f0f72 100644
--- a/src/starboard/shared/uwp/starboard_platform.gypi
+++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -22,6 +22,8 @@
       'system_clear_platform_error.cc',
       'system_get_device_type.cc',
       'system_get_property.cc',
+      'system_get_total_cpu_memory.cc',
+      'system_get_used_cpu_memory.cc',
       'system_raise_platform_error.cc',
       'window_create.cc',
       'window_destroy.cc',
diff --git a/src/starboard/shared/uwp/system_get_device_type.cc b/src/starboard/shared/uwp/system_get_device_type.cc
index 81179ca..33ce323 100644
--- a/src/starboard/shared/uwp/system_get_device_type.cc
+++ b/src/starboard/shared/uwp/system_get_device_type.cc
@@ -23,7 +23,7 @@
 using Windows::System::Profile::AnalyticsVersionInfo;
 
 SbSystemDeviceType SbSystemGetDeviceType() {
-  AnalyticsVersionInfo ^ version_info = AnalyticsInfo::VersionInfo;
+  AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
   std::string family = starboard::shared::win32::platformStringToString(
       version_info->DeviceFamily);
 
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/uwp/system_get_total_cpu_memory.cc
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/uwp/system_get_total_cpu_memory.cc
index be4e805..293fd7d 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/uwp/system_get_total_cpu_memory.cc
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#include "starboard/system.h"
 
-#include "starboard/shared/win32/atomic_public.h"
+using Windows::System::MemoryManager;
 
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+int64_t SbSystemGetTotalCPUMemory() {
+  return static_cast<int64_t>(MemoryManager::AppMemoryUsageLimit);
+}
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/uwp/system_get_used_cpu_memory.cc
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/uwp/system_get_used_cpu_memory.cc
index be4e805..adc9c0d 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/uwp/system_get_used_cpu_memory.cc
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#include "starboard/system.h"
 
-#include "starboard/shared/win32/atomic_public.h"
+using Windows::System::MemoryManager;
 
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+int64_t SbSystemGetUsedCPUMemory() {
+  return static_cast<int64_t>(MemoryManager::AppMemoryUsage);
+}
diff --git a/src/starboard/shared/win32/audio_decoder.cc b/src/starboard/shared/win32/audio_decoder.cc
index d6749f2..879202c 100644
--- a/src/starboard/shared/win32/audio_decoder.cc
+++ b/src/starboard/shared/win32/audio_decoder.cc
@@ -56,19 +56,24 @@
 };
 
 AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
-                           const SbMediaAudioHeader& audio_header)
-    : sample_type_(kSbMediaAudioSampleTypeFloat32),
-      stream_ended_(false),
-      audio_codec_(audio_codec),
-      audio_header_(audio_header) {
+                           const SbMediaAudioHeader& audio_header,
+                           SbDrmSystem drm_system)
+    : audio_codec_(audio_codec),
+      audio_header_(audio_header),
+      drm_system_(drm_system),
+      sample_type_(kSbMediaAudioSampleTypeFloat32),
+      stream_ended_(false) {
   SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
   decoder_impl_ = AbstractWin32AudioDecoder::Create(
-      audio_codec_, GetStorageType(), GetSampleType(), audio_header_);
+      audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
+      drm_system_);
   decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
   callback_scheduler_.reset(new CallbackScheduler());
 }
 
 AudioDecoder::~AudioDecoder() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   decoder_thread_.reset(nullptr);
   decoder_impl_.reset(nullptr);
   callback_scheduler_.reset(nullptr);
@@ -76,7 +81,9 @@
 
 void AudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer,
                           const Closure& consumed_cb) {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
   SB_DCHECK(input_buffer);
+
   callback_scheduler_->SetCallbackOnce(consumed_cb);
   callback_scheduler_->OnCallbackSignaled();
   const bool can_take_more_data = decoder_thread_->QueueInput(input_buffer);
@@ -91,22 +98,29 @@
 }
 
 void AudioDecoder::WriteEndOfStream() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   ::starboard::ScopedLock lock(mutex_);
   stream_ended_ = true;
   decoder_thread_->QueueEndOfStream();
 }
 
 scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   DecodedAudioPtr data = decoded_data_.PopFront();
   SB_DCHECK(data);
   return data;
 }
 
 void AudioDecoder::Reset() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   decoder_thread_.reset(nullptr);
   decoder_impl_.reset(nullptr);
   decoder_impl_ = AbstractWin32AudioDecoder::Create(
-      audio_codec_, GetStorageType(), GetSampleType(), audio_header_);
+      audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
+      drm_system_);
   decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
   decoded_data_.Clear();
   stream_ended_ = false;
@@ -114,14 +128,20 @@
 }
 
 SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   return sample_type_;
 }
 
 int AudioDecoder::GetSamplesPerSecond() const {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   return audio_header_.samples_per_second;
 }
 
 void AudioDecoder::Initialize(const Closure& output_cb) {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   SB_DCHECK(output_cb.is_valid());
   SB_DCHECK(!output_cb_.is_valid());
   output_cb_ = output_cb;
diff --git a/src/starboard/shared/win32/audio_decoder.h b/src/starboard/shared/win32/audio_decoder.h
index f036287..5bd335c 100644
--- a/src/starboard/shared/win32/audio_decoder.h
+++ b/src/starboard/shared/win32/audio_decoder.h
@@ -18,11 +18,13 @@
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
+#include "starboard/drm.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/job_queue.h"
+#include "starboard/shared/starboard/thread_checker.h"
 #include "starboard/shared/win32/atomic_queue.h"
 #include "starboard/shared/win32/audio_decoder_thread.h"
 #include "starboard/shared/win32/media_common.h"
@@ -32,16 +34,14 @@
 namespace shared {
 namespace win32 {
 
-using JobQueue = ::starboard::shared::starboard::player::JobQueue;
-using JobOwner = JobQueue::JobOwner;
-
 class AudioDecoder
     : public ::starboard::shared::starboard::player::filter::AudioDecoder,
-      private JobOwner,
+      private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private AudioDecodedCallback {
  public:
   AudioDecoder(SbMediaAudioCodec audio_codec,
-               const SbMediaAudioHeader& audio_header);
+               const SbMediaAudioHeader& audio_header,
+               SbDrmSystem drm_system);
   ~AudioDecoder() SB_OVERRIDE;
 
   void Decode(const scoped_refptr<InputBuffer>& input_buffer,
@@ -60,9 +60,13 @@
 
  private:
   class CallbackScheduler;
-  SbMediaAudioHeader audio_header_;
-  SbMediaAudioSampleType sample_type_;
+
+  ::starboard::shared::starboard::ThreadChecker thread_checker_;
+
   SbMediaAudioCodec audio_codec_;
+  SbMediaAudioHeader audio_header_;
+  SbDrmSystem drm_system_;
+  SbMediaAudioSampleType sample_type_;
   bool stream_ended_;
 
   AtomicQueue<DecodedAudioPtr> decoded_data_;
@@ -71,7 +75,7 @@
   scoped_ptr<AudioDecoderThread> decoder_thread_;
   Closure output_cb_;
 
-  ::starboard::Mutex mutex_;
+  Mutex mutex_;
 };
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc
index c5d8de2..5967211 100644
--- a/src/starboard/shared/win32/audio_sink.cc
+++ b/src/starboard/shared/win32/audio_sink.cc
@@ -72,6 +72,10 @@
     ScopedLock lock(mutex_);
     playback_rate_ = playback_rate;
   }
+  void SetVolume(double volume) SB_OVERRIDE {
+    ScopedLock lock(mutex_);
+    volume_ = volume;
+  }
 
  private:
   static void* ThreadEntryPoint(void* context);
@@ -99,6 +103,7 @@
   ::starboard::Mutex mutex_;
   bool destroying_;
   double playback_rate_;
+  double volume_;
 };
 
 XAudioAudioSink::XAudioAudioSink(
@@ -118,7 +123,8 @@
       frame_buffers_size_in_frames_(frame_buffers_size_in_frames),
       wfx_(wfx),
       destroying_(false),
-      playback_rate_(1.0) {
+      playback_rate_(1.0),
+      volume_(1.0) {
   // TODO: Check MaxFrequencyRatio
   CHECK_HRESULT_OK(
       type_->x_audio2_->CreateSourceVoice(&source_voice_, &wfx, 0,
@@ -187,6 +193,7 @@
     int frames_in_buffer, offset_in_frames;
     bool is_playing, is_eos_reached;
     bool is_playback_rate_zero;
+    // TODO: Support |volume_| here as well...
     {
       ScopedLock lock(mutex_);
       is_playback_rate_zero = playback_rate_ == 0.0;
diff --git a/src/starboard/shared/win32/decode_target_internal.cc b/src/starboard/shared/win32/decode_target_internal.cc
index 70b8e32..8188621 100644
--- a/src/starboard/shared/win32/decode_target_internal.cc
+++ b/src/starboard/shared/win32/decode_target_internal.cc
@@ -26,6 +26,7 @@
 #include "third_party/angle/include/EGL/egl.h"
 #include "third_party/angle/include/EGL/eglext.h"
 #include "third_party/angle/include/GLES2/gl2.h"
+#include "third_party/angle/include/GLES2/gl2ext.h"
 
 using Microsoft::WRL::ComPtr;
 using starboard::shared::win32::VideoFramePtr;
@@ -73,24 +74,24 @@
   SbDecodeTargetInfoPlane* planeY = &(info.planes[kSbDecodeTargetPlaneY]);
   SbDecodeTargetInfoPlane* planeUV = &(info.planes[kSbDecodeTargetPlaneUV]);
 
-  planeY->width = texture_desc.Width;
-  planeY->height = texture_desc.Height;
+  planeY->width = info.width;
+  planeY->height = info.height;
   planeY->content_region.left = 0;
-  planeY->content_region.top = 0;
-  planeY->content_region.right = texture_desc.Width;
-  planeY->content_region.bottom = texture_desc.Height;
+  planeY->content_region.top = info.height;
+  planeY->content_region.right = frame->width();
+  planeY->content_region.bottom = info.height - frame->height();
 
-  planeUV->width = texture_desc.Width / 2;
-  planeUV->height = texture_desc.Height / 2;
-  planeUV->content_region.left = 0;
-  planeUV->content_region.top = 0;
-  planeUV->content_region.right = texture_desc.Width / 2;
-  planeUV->content_region.bottom = texture_desc.Height / 2;
+  planeUV->width = info.width / 2;
+  planeUV->height = info.height / 2;
+  planeUV->content_region.left = planeY->content_region.left / 2;
+  planeUV->content_region.top = planeY->content_region.top / 2;
+  planeUV->content_region.right = planeY->content_region.right / 2;
+  planeUV->content_region.bottom = planeY->content_region.bottom / 2;
 
   EGLint luma_texture_attributes[] = {EGL_WIDTH,
-                                      static_cast<EGLint>(texture_desc.Width),
+                                      static_cast<EGLint>(info.width),
                                       EGL_HEIGHT,
-                                      static_cast<EGLint>(texture_desc.Height),
+                                      static_cast<EGLint>(info.height),
                                       EGL_TEXTURE_TARGET,
                                       EGL_TEXTURE_2D,
                                       EGL_TEXTURE_FORMAT,
@@ -119,16 +120,16 @@
                                  dxgi_buffer.GetAddressOf());
   SB_DCHECK(SUCCEEDED(hr));
 
-  EGLSurface surface = eglCreatePbufferFromClientBuffer(
-      display, EGL_D3D_TEXTURE_ANGLE, d3texture.Get(), config,
-      luma_texture_attributes);
+  surface[0] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+                                                d3texture.Get(), config,
+                                                luma_texture_attributes);
 
-  SB_DCHECK(surface != EGL_NO_SURFACE);
+  SB_DCHECK(surface[0] != EGL_NO_SURFACE);
 
   glBindTexture(GL_TEXTURE_2D, gl_textures[0]);
   SB_DCHECK(glGetError() == GL_NO_ERROR);
 
-  ok = eglBindTexImage(display, surface, EGL_BACK_BUFFER);
+  ok = eglBindTexImage(display, surface[0], EGL_BACK_BUFFER);
   SB_DCHECK(ok);
 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -136,6 +137,7 @@
 
   planeY->texture = gl_textures[0];
   planeY->gl_texture_target = GL_TEXTURE_2D;
+  planeY->gl_texture_format = GL_RED_EXT;
 
   // This tells ANGLE that the texture it creates should draw
   // the chroma channel on R8G8.
@@ -145,24 +147,24 @@
 
   EGLint chroma_texture_attributes[] = {
       EGL_WIDTH,
-      static_cast<EGLint>(texture_desc.Width) / 2,
+      static_cast<EGLint>(info.width) / 2,
       EGL_HEIGHT,
-      static_cast<EGLint>(texture_desc.Height) / 2,
+      static_cast<EGLint>(info.height) / 2,
       EGL_TEXTURE_TARGET,
       EGL_TEXTURE_2D,
       EGL_TEXTURE_FORMAT,
       EGL_TEXTURE_RGBA,
       EGL_NONE};
-  surface = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
-                                             d3texture.Get(), config,
-                                             chroma_texture_attributes);
+  surface[1] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+                                                d3texture.Get(), config,
+                                                chroma_texture_attributes);
 
-  SB_DCHECK(surface != EGL_NO_SURFACE);
+  SB_DCHECK(surface[1] != EGL_NO_SURFACE);
 
   glBindTexture(GL_TEXTURE_2D, gl_textures[1]);
   SB_DCHECK(glGetError() == GL_NO_ERROR);
 
-  ok = eglBindTexImage(display, surface, EGL_BACK_BUFFER);
+  ok = eglBindTexImage(display, surface[1], EGL_BACK_BUFFER);
   SB_DCHECK(ok);
 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -170,6 +172,7 @@
 
   planeUV->texture = gl_textures[1];
   planeUV->gl_texture_target = GL_TEXTURE_2D;
+  planeUV->gl_texture_format = GL_RG_EXT;
 
   hr = d3texture->SetPrivateData(kCobaltDxgiBuffer, 0, nullptr);
   SB_DCHECK(SUCCEEDED(hr));
@@ -178,6 +181,14 @@
 SbDecodeTargetPrivate::~SbDecodeTargetPrivate() {
   glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneY].texture));
   glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneUV].texture));
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+  eglReleaseTexImage(display, surface[0], EGL_BACK_BUFFER);
+  eglDestroySurface(display, surface[0]);
+
+  eglReleaseTexImage(display, surface[1], EGL_BACK_BUFFER);
+  eglDestroySurface(display, surface[1]);
 }
 
 void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
diff --git a/src/starboard/shared/win32/decode_target_internal.h b/src/starboard/shared/win32/decode_target_internal.h
index 6204200..81aa66e 100644
--- a/src/starboard/shared/win32/decode_target_internal.h
+++ b/src/starboard/shared/win32/decode_target_internal.h
@@ -23,6 +23,9 @@
   // Publicly accessible information about the decode target.
   SbDecodeTargetInfo info;
   ::starboard::shared::win32::VideoFramePtr frame;
+  // EGLSurface is defined as void* in "third_party/angle/include/EGL/egl.h".
+  // Use void* directly here to avoid `egl.h` being included broadly.
+  void* surface[2];
   explicit SbDecodeTargetPrivate(starboard::shared::win32::VideoFramePtr frame);
   ~SbDecodeTargetPrivate();
 };
diff --git a/src/starboard/shared/win32/gyp_configuration.py b/src/starboard/shared/win32/gyp_configuration.py
index 0bf0e26..93f92f1 100644
--- a/src/starboard/shared/win32/gyp_configuration.py
+++ b/src/starboard/shared/win32/gyp_configuration.py
@@ -85,5 +85,5 @@
   def GetToolchain(self):
     sys.path.append(
         os.path.join(STARBOARD_ROOT, 'shared', 'msvc', 'uwp'))
-    from toolchain import MSVCUWPToolchain  # pylint: disable=g-import-not-at-top,g-bad-import-order
+    from msvc_toolchain import MSVCUWPToolchain  # pylint: disable=g-import-not-at-top,g-bad-import-order
     return MSVCUWPToolchain()
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/shared/win32/media_is_output_protected.cc
similarity index 76%
copy from src/starboard/win/lib/atomic_public.h
copy to src/starboard/shared/win32/media_is_output_protected.cc
index be4e805..73d2f07 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/shared/win32/media_is_output_protected.cc
@@ -1,20 +1,21 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

+// You may obtain a copy of the License at

+//

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

+//

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

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

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

+// See the License for the specific language governing permissions and

+// limitations under the License.

+

+#include "starboard/media.h"

+

+bool SbMediaIsOutputProtected() {

+  // Pretend that HDCP is always on.

+  // TODO: Fix this with proper HDCP query.

+  return true;

+}

diff --git a/src/starboard/shared/win32/player_components_impl.cc b/src/starboard/shared/win32/player_components_impl.cc
index cc86d10..fa2c4af 100644
--- a/src/starboard/shared/win32/player_components_impl.cc
+++ b/src/starboard/shared/win32/player_components_impl.cc
@@ -35,9 +35,13 @@
   using VideoRendererImpl = ::starboard::shared::win32::VideoRendererImpl;
 
   AudioDecoderImpl* audio_decoder = new AudioDecoderImpl(
-      audio_parameters.audio_codec, audio_parameters.audio_header);
+      audio_parameters.audio_codec, audio_parameters.audio_header,
+      audio_parameters.drm_system);
 
-  VideoDecoderImpl* video_decoder = new VideoDecoderImpl(video_parameters);
+  VideoDecoderImpl* video_decoder = new VideoDecoderImpl(
+      video_parameters.video_codec, video_parameters.output_mode,
+      video_parameters.decode_target_graphics_context_provider,
+      video_parameters.drm_system);
 
   AudioRendererImpl* audio_renderer =
       new AudioRendererImpl(scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
diff --git a/src/starboard/shared/win32/video_decoder.cc b/src/starboard/shared/win32/video_decoder.cc
index 40fd85d..0a08c9a 100644
--- a/src/starboard/shared/win32/video_decoder.cc
+++ b/src/starboard/shared/win32/video_decoder.cc
@@ -24,17 +24,24 @@
 namespace shared {
 namespace win32 {
 
-VideoDecoder::VideoDecoder(const VideoParameters& params)
-    : video_codec_(params.video_codec),
+VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
+                           SbPlayerOutputMode output_mode,
+                           SbDecodeTargetGraphicsContextProvider*
+                               decode_target_graphics_context_provider,
+                           SbDrmSystem drm_system)
+    : video_codec_(video_codec),
+      drm_system_(drm_system),
       host_(NULL),
-      output_mode_(params.output_mode),
+      output_mode_(output_mode),
       decode_target_graphics_context_provider_(
-          params.decode_target_graphics_context_provider) {
-  impl_ = AbstractWin32VideoDecoder::Create(video_codec_);
+          decode_target_graphics_context_provider) {
+  impl_ = AbstractWin32VideoDecoder::Create(video_codec_, drm_system_);
   video_decoder_thread_.reset(new VideoDecoderThread(impl_.get(), this));
 }
 
 VideoDecoder::~VideoDecoder() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   video_decoder_thread_.reset(nullptr);
   impl_.reset(nullptr);
 }
@@ -71,7 +78,7 @@
   SB_DCHECK(host_);
   video_decoder_thread_.reset(nullptr);
   impl_.reset(nullptr);
-  impl_ = AbstractWin32VideoDecoder::Create(video_codec_);
+  impl_ = AbstractWin32VideoDecoder::Create(video_codec_, drm_system_);
   video_decoder_thread_.reset(new VideoDecoderThread(impl_.get(), this));
 }
 
@@ -83,7 +90,7 @@
 }
 
 void VideoDecoder::OnVideoDecoded(VideoFramePtr data) {
-  Status sts = data->IsEndOfStream() ? kBufferFull : kNeedMoreInput;
+  Status sts = (data && data->IsEndOfStream()) ? kBufferFull : kNeedMoreInput;
   host_->OnDecoderStatusUpdate(sts, data);
 }
 
diff --git a/src/starboard/shared/win32/video_decoder.h b/src/starboard/shared/win32/video_decoder.h
index 1fd2bfa..5d3734b 100644
--- a/src/starboard/shared/win32/video_decoder.h
+++ b/src/starboard/shared/win32/video_decoder.h
@@ -18,6 +18,7 @@
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
+#include "starboard/drm.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
@@ -38,7 +39,11 @@
       private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private VideoDecodedCallback {
  public:
-  explicit VideoDecoder(const VideoParameters& params);
+  VideoDecoder(SbMediaVideoCodec video_codec,
+               SbPlayerOutputMode output_mode,
+               SbDecodeTargetGraphicsContextProvider*
+                   decode_target_graphics_context_provider,
+               SbDrmSystem drm_system);
   ~VideoDecoder() SB_OVERRIDE;
 
   void SetHost(Host* host) SB_OVERRIDE;
@@ -53,22 +58,23 @@
   void OnVideoDecoded(VideoFramePtr data) SB_OVERRIDE;
 
  private:
+  ::starboard::shared::starboard::ThreadChecker thread_checker_;
+
   // These variables will be initialized inside ctor or SetHost() and will not
   // be changed during the life time of this class.
   const SbMediaVideoCodec video_codec_;
+  SbDrmSystem drm_system_;
   Host* host_;
 
   // Decode-to-texture related state.
-  SbPlayerOutputMode output_mode_;
-
-  SbDecodeTargetGraphicsContextProvider*
+  const SbPlayerOutputMode output_mode_;
+  SbDecodeTargetGraphicsContextProvider* const
       decode_target_graphics_context_provider_;
 
   scoped_ptr<AbstractWin32VideoDecoder> impl_;
   AtomicQueue<VideoFramePtr> decoded_data_;
-  ::starboard::Mutex mutex_;
+  Mutex mutex_;
   scoped_ptr<VideoDecoderThread> video_decoder_thread_;
-  ::starboard::shared::starboard::ThreadChecker thread_checker_;
 };
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/video_decoder_thread.cc b/src/starboard/shared/win32/video_decoder_thread.cc
index 64a7b2e..d2f8ba2 100644
--- a/src/starboard/shared/win32/video_decoder_thread.cc
+++ b/src/starboard/shared/win32/video_decoder_thread.cc
@@ -102,6 +102,7 @@
     if (number_written > 0) {
       processing_elements_.fetch_sub(static_cast<int32_t>(number_written));
       work_done = true;
+      callback_->OnVideoDecoded(NULL);
     }
 
     while (VideoFramePtr decoded_datum =
diff --git a/src/starboard/shared/win32/win32_audio_decoder.cc b/src/starboard/shared/win32/win32_audio_decoder.cc
index 0b85f30..4142764 100644
--- a/src/starboard/shared/win32/win32_audio_decoder.cc
+++ b/src/starboard/shared/win32/win32_audio_decoder.cc
@@ -103,18 +103,19 @@
 };
 
 class AbstractWin32AudioDecoderImpl : public AbstractWin32AudioDecoder,
-                                    public MediaBufferConsumerInterface {
+                                      public MediaBufferConsumerInterface {
  public:
   AbstractWin32AudioDecoderImpl(SbMediaAudioCodec codec,
-                              SbMediaAudioFrameStorageType audio_frame_fmt,
-                              SbMediaAudioSampleType sample_type,
-                              const SbMediaAudioHeader& audio_header)
+                                SbMediaAudioFrameStorageType audio_frame_fmt,
+                                SbMediaAudioSampleType sample_type,
+                                const SbMediaAudioHeader& audio_header,
+                                SbDrmSystem drm_system)
       : codec_(codec),
         audio_frame_fmt_(audio_frame_fmt),
         sample_type_(sample_type),
         audio_header_(audio_header) {
     MediaBufferConsumerInterface* media_cb = this;
-    impl_.reset(new DecoderImpl("audio", media_cb));
+    impl_.reset(new DecoderImpl("audio", media_cb, drm_system));
     EnsureAudioDecoderCreated();
   }
 
@@ -151,6 +152,8 @@
     media_buffer->Unlock();
   }
 
+  void OnNewOutputType(const ComPtr<IMFMediaType>& /*type*/) override {}
+
   ComPtr<IMFMediaType> Configure(IMFTransform* decoder) {
     ComPtr<IMFMediaType> input_type;
     HRESULT hr = MFCreateMediaType(&input_type);
@@ -209,7 +212,6 @@
     SB_DCHECK(decoder);
 
     impl_->set_decoder(decoder);
-    impl_->ActivateDecryptor(media_type);
 
     // TODO: MFWinAudioFormat_PCM?
     ComPtr<IMFMediaType> output_type =
@@ -234,17 +236,40 @@
     const int size = buff.size();
     const int64_t media_timestamp = buff.pts();
 
-    // These parameters are used for decryption. But these are not used right
-    // now and so remain empty.
     std::vector<uint8_t> key_id;
     std::vector<uint8_t> iv;
     std::vector<Subsample> subsamples;
 
+    const SbDrmSampleInfo* drm_info = buff.drm_info();
+
+    if (drm_info != NULL && drm_info->initialization_vector_size != 0) {
+      key_id.assign(drm_info->identifier,
+                    drm_info->identifier + drm_info->identifier_size);
+      iv.assign(drm_info->initialization_vector,
+                drm_info->initialization_vector +
+                    drm_info->initialization_vector_size);
+      subsamples.reserve(drm_info->subsample_count);
+      for (int32_t i = 0; i < drm_info->subsample_count; ++i) {
+        Subsample subsample = {
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].clear_byte_count),
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].encrypted_byte_count)};
+        subsamples.push_back(subsample);
+      }
+    }
+
     const std::int64_t win32_time_stamp = ConvertToWin32Time(media_timestamp);
 
     // Adjust the offset for 7 bytes to remove the ADTS header.
-    const uint8_t* audio_start = static_cast<const uint8_t*>(data) + 7;
-    const int audio_size = size - 7;
+    const int kADTSHeaderSize = 7;
+    const uint8_t* audio_start =
+        static_cast<const uint8_t*>(data) + kADTSHeaderSize;
+    const int audio_size = size - kADTSHeaderSize;
+    if (!subsamples.empty()) {
+      SB_DCHECK(subsamples[0].clear_bytes == kADTSHeaderSize);
+      subsamples[0].clear_bytes = 0;
+    }
 
     const bool write_ok = impl_->TryWriteInputBuffer(
         audio_start, audio_size, win32_time_stamp, key_id, iv, subsamples);
@@ -282,10 +307,11 @@
     SbMediaAudioCodec code,
     SbMediaAudioFrameStorageType audio_frame_fmt,
     SbMediaAudioSampleType sample_type,
-    const SbMediaAudioHeader& audio_header) {
-  return scoped_ptr<AbstractWin32AudioDecoder>(new
-    AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
-      audio_header));
+    const SbMediaAudioHeader& audio_header,
+    SbDrmSystem drm_system) {
+  return scoped_ptr<AbstractWin32AudioDecoder>(
+      new AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
+                                        audio_header, drm_system));
 }
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/win32_audio_decoder.h b/src/starboard/shared/win32/win32_audio_decoder.h
index 4cc5f27..3bfe78c 100644
--- a/src/starboard/shared/win32/win32_audio_decoder.h
+++ b/src/starboard/shared/win32/win32_audio_decoder.h
@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
@@ -35,7 +36,8 @@
       SbMediaAudioCodec codec,
       SbMediaAudioFrameStorageType audio_frame_fmt,
       SbMediaAudioSampleType sample_type,
-      const SbMediaAudioHeader& audio_header);
+      const SbMediaAudioHeader& audio_header,
+      SbDrmSystem drm_system);
   virtual ~AbstractWin32AudioDecoder() {}
 
   // INPUT:
diff --git a/src/starboard/shared/win32/win32_decoder_impl.cc b/src/starboard/shared/win32/win32_decoder_impl.cc
index b06fcdc..b41b418 100644
--- a/src/starboard/shared/win32/win32_decoder_impl.cc
+++ b/src/starboard/shared/win32/win32_decoder_impl.cc
@@ -117,11 +117,15 @@
 }  // namespace
 
 DecoderImpl::DecoderImpl(const std::string& type,
-                         MediaBufferConsumerInterface* media_buffer_consumer)
+                         MediaBufferConsumerInterface* media_buffer_consumer,
+                         SbDrmSystem drm_system)
     : type_(type),
       media_buffer_consumer_(media_buffer_consumer),
       discontinuity_(false) {
   SB_DCHECK(media_buffer_consumer_);
+  drm_system_ =
+      static_cast<::starboard::xb1::shared::playready::SbDrmSystemPlayready*>(
+          drm_system);
 }
 
 DecoderImpl::~DecoderImpl() {
@@ -137,13 +141,29 @@
   return decoder;
 }
 
-void DecoderImpl::ActivateDecryptor(ComPtr<IMFMediaType> input_type) {
+void DecoderImpl::ActivateDecryptor() {
+  SB_DCHECK(decoder_);
+
   if (!decryptor_) {
     return;
   }
 
-  HRESULT hr = decryptor_->SetInputType(kStreamId, input_type.Get(),
-                                        0);  // MFT_SET_TYPE_TEST_FLAGS.
+  ComPtr<IMFMediaType> decryptor_input_type;
+
+  HRESULT hr = decoder_->GetInputCurrentType(
+      kStreamId, decryptor_input_type.GetAddressOf());
+  CheckResult(hr);
+
+  GUID original_sub_type;
+  {
+    ComPtr<IMFMediaType> output_type;
+    hr = decoder_->GetOutputCurrentType(kStreamId, output_type.GetAddressOf());
+    CheckResult(hr);
+    output_type->GetGUID(MF_MT_SUBTYPE, &original_sub_type);
+  }
+
+  const DWORD kFlags = 0;
+  hr = decryptor_->SetInputType(kStreamId, decryptor_input_type.Get(), kFlags);
   CheckResult(hr);
 
   // Ensure that the decryptor and the decoder agrees on the protection of
@@ -158,7 +178,6 @@
   BYTE* crypt_seed = NULL;
   DWORD crypt_seed_size = 0;
   ComPtr<IMFMediaType> decoder_input_type;
-  ComPtr<IMFMediaType> decoder_output_type;
 
   hr = decryptor_.As(&decryption_sample_protection);
   CheckResult(hr);
@@ -211,6 +230,21 @@
   // Start the decryptor, note that this should be better abstracted.
   SendMFTMessage(decryptor_.Get(), MFT_MESSAGE_NOTIFY_BEGIN_STREAMING);
   SendMFTMessage(decryptor_.Get(), MFT_MESSAGE_NOTIFY_START_OF_STREAM);
+
+  for (int index = 0;; ++index) {
+    ComPtr<IMFMediaType> output_type;
+    hr = decoder_->GetOutputAvailableType(0, index, &output_type);
+    if (SUCCEEDED(hr)) {
+      GUID sub_type;
+      output_type->GetGUID(MF_MT_SUBTYPE, &sub_type);
+      if (IsEqualGUID(sub_type, original_sub_type)) {
+        hr = decoder_->SetOutputType(0, output_type.Get(), 0);
+        CheckResult(hr);
+        break;
+      }
+    }
+    output_type.Reset();
+  }
 }
 
 bool DecoderImpl::TryWriteInputBuffer(
@@ -236,6 +270,19 @@
   // Better check the key id size is 16 and iv size is 8 or 16.
   bool encrypted = !key_id.empty() && !iv.empty();
   if (encrypted) {
+    if (!decryptor_) {
+      scoped_refptr<::starboard::xb1::shared::playready::PlayreadyLicense>
+          license = drm_system_->GetLicense(key_id.data(),
+                                            static_cast<int>(key_id.size()));
+      if (license && license->usable()) {
+        decryptor_ = license->decryptor();
+        ActivateDecryptor();
+      }
+    }
+    if (!decryptor_) {
+      SB_NOTREACHED();
+      return false;
+    }
     size_t iv_size = iv.size();
     const char kEightZeros[8] = {0};
     if (iv_size == 16 && SbMemoryCompare(iv.data() + 8, kEightZeros, 8) == 0) {
@@ -315,27 +362,33 @@
 
 bool DecoderImpl::DeliverOneOutputOnAllTransforms() {
   SB_DCHECK(decoder_);
-  bool delivered = false;
   if (decryptor_) {
-    if (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decryptor_)) {
-      HRESULT hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+    if (!cached_decryptor_output_) {
+      cached_decryptor_output_ = DeliverOutputOnTransform(decryptor_);
+    }
+    while (cached_decryptor_output_) {
+      HRESULT hr =
+          decoder_->ProcessInput(kStreamId, cached_decryptor_output_.Get(), 0);
       if (hr == MF_E_NOTACCEPTING) {
-        // The protocol says that when ProcessInput() returns MF_E_NOTACCEPTING,
-        // there must be some output available. Retrieve the output and the next
-        // ProcessInput() should succeed.
-        ComPtr<IMFSample> sample_inner = DeliverOutputOnTransform(decoder_);
-        if (sample_inner) {
-          DeliverDecodedSample(sample_inner);
-          delivered = true;
-        }
-        hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+        break;
+      } else {
         CheckResult(hr);
-        return delivered;
+        cached_decryptor_output_ = DeliverOutputOnTransform(decryptor_);
       }
-      CheckResult(hr);
-      delivered = true;
+    }
+    if (cached_decryptor_output_) {
+      // The protocol says that when ProcessInput() returns MF_E_NOTACCEPTING,
+      // there must be some output available. Retrieve the output and the next
+      // ProcessInput() should succeed.
+      ComPtr<IMFSample> decoder_output = DeliverOutputOnTransform(decoder_);
+      if (decoder_output) {
+        DeliverDecodedSample(decoder_output);
+      }
+      return decoder_output != NULL;
     }
   }
+
+  bool delivered = false;
   if (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decoder_)) {
     DeliverDecodedSample(sample);
     delivered = true;
@@ -371,18 +424,6 @@
   HRESULT hr = transform->GetOutputStreamInfo(kStreamId, &output_stream_info);
   CheckResult(hr);
 
-  // Each media sample (IMFSample interface) of output data from the MFT
-  // contains complete, unbroken units of data. The definition of a unit
-  // of data depends on the media type: For uncompressed video, a video
-  // frame; for compressed data, a compressed packet; for uncompressed audio,
-  // a single audio frame.
-  //
-  // For uncompressed audio formats, this flag is always implied. (It is valid
-  // to set the flag, but not required.) An uncompressed audio frame should
-  // never span more than one media sample.
-  SB_DCHECK((output_stream_info.dwFlags & MFT_OUTPUT_STREAM_WHOLE_SAMPLES) !=
-            0);
-
   if (StreamAllocatesMemory(output_stream_info.dwFlags)) {
     // Try to let the IMFTransform allocate the memory if possible.
     return;
@@ -431,9 +472,21 @@
 
     hr = transform->SetOutputType(kStreamId, media_type.Get(), 0);
     CheckResult(hr);
-    return NULL;
-  } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT ||
-             output_data_buffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) {
+
+    media_buffer_consumer_->OnNewOutputType(media_type);
+
+    hr = transform->ProcessOutput(kFlags, kNumberOfBuffers, &output_data_buffer,
+                                  &status);
+
+    SB_DCHECK(!output_data_buffer.pEvents);
+
+    output = output_data_buffer.pSample;
+    ReleaseIfNotNull(&output_data_buffer.pEvents);
+    ReleaseIfNotNull(&output_data_buffer.pSample);
+  }
+
+  if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT ||
+      output_data_buffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) {
     return NULL;
   }
 
diff --git a/src/starboard/shared/win32/win32_decoder_impl.h b/src/starboard/shared/win32/win32_decoder_impl.h
index 38374ff..cef0d5d 100644
--- a/src/starboard/shared/win32/win32_decoder_impl.h
+++ b/src/starboard/shared/win32/win32_decoder_impl.h
@@ -25,10 +25,14 @@
 #include <vector>
 
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/win32/media_common.h"
 #include "starboard/types.h"
 
+// TODO: Win32 code shouldn't depend on Xb1 code.  Refactor this.
+#include "starboard/xb1/shared/playready/drm_system_playready.h"
+
 namespace starboard {
 namespace shared {
 namespace win32 {
@@ -38,6 +42,8 @@
   virtual void Consume(Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer,
                        int64_t win32_timestamp) = 0;
 
+  virtual void OnNewOutputType(const ComPtr<IMFMediaType>& type) = 0;
+
  protected:
   ~MediaBufferConsumerInterface() {}
 };
@@ -50,12 +56,13 @@
 class DecoderImpl {
  public:
   DecoderImpl(const std::string& type,
-              MediaBufferConsumerInterface* media_buffer_consumer);
+              MediaBufferConsumerInterface* media_buffer_consumer,
+              SbDrmSystem drm_system);
   ~DecoderImpl();
 
   static Microsoft::WRL::ComPtr<IMFTransform> CreateDecoder(CLSID clsid);
 
-  void ActivateDecryptor(Microsoft::WRL::ComPtr<IMFMediaType> input_type);
+  void ActivateDecryptor();
   bool TryWriteInputBuffer(const void* data,
                            int size,
                            std::int64_t win32_timestamp,
@@ -84,9 +91,11 @@
   const std::string type_;  // For debugging purpose.
   // This is the target for the all completed media buffers.
   MediaBufferConsumerInterface* media_buffer_consumer_;
+  ::starboard::xb1::shared::playready::SbDrmSystemPlayready* drm_system_;
   bool discontinuity_;
 
   Microsoft::WRL::ComPtr<IMFTransform> decryptor_;
+  Microsoft::WRL::ComPtr<IMFSample> cached_decryptor_output_;
   Microsoft::WRL::ComPtr<IMFTransform> decoder_;
 };
 
diff --git a/src/starboard/shared/win32/win32_video_decoder.cc b/src/starboard/shared/win32/win32_video_decoder.cc
index 93bc487..667b2e5 100644
--- a/src/starboard/shared/win32/win32_video_decoder.cc
+++ b/src/starboard/shared/win32/win32_video_decoder.cc
@@ -36,31 +36,22 @@
 // This magic number is taken directly from sample from MS.  Can be further
 // tuned in case if there is playback issues.
 const int kSampleAllocatorFramesMax = 5;
+// This is the minimum allocated frames in the output ring buffer.  Can be
+// further tuned to save memory.  Note that use a value that is too small leads
+// to hang when calling ProcessOutput().
+const int kMinimumOutputSampleCount = 10;
 
 // CLSID_CMSVideoDecoderMFT {62CE7E72-4C71-4D20-B15D-452831A87D9D}
 const GUID CLSID_VideoDecoder = {0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45,
                                  0x28,       0x31,   0xA8,   0x7D, 0x9D};
 
-ComPtr<ID3D11Texture2D> GetTexture2D(ComPtr<IMFMediaBuffer> media_buffer) {
-  ComPtr<IMFDXGIBuffer> dxgi_buffer;
-  HRESULT hr = media_buffer.As(&dxgi_buffer);
-  CheckResult(hr);
-  SB_DCHECK(dxgi_buffer.Get());
-
-  ComPtr<ID3D11Texture2D> dx_texture;
-  hr = dxgi_buffer->GetResource(IID_PPV_ARGS(&dx_texture));
-  CheckResult(hr);
-  return dx_texture;
-}
-
 class VideoFrameFactory {
  public:
   static VideoFramePtr Construct(SbMediaTime timestamp,
-                                 ComPtr<IMFMediaBuffer> media_buffer) {
-    ComPtr<ID3D11Texture2D> texture = GetTexture2D(media_buffer);
-    D3D11_TEXTURE2D_DESC tex_desc;
-    texture->GetDesc(&tex_desc);
-    VideoFramePtr out(new VideoFrame(tex_desc.Width, tex_desc.Height,
+                                 ComPtr<IMFMediaBuffer> media_buffer,
+                                 uint32_t width,
+                                 uint32_t height) {
+    VideoFramePtr out(new VideoFrame(width, height,
                                      timestamp, media_buffer.Detach(),
                                      nullptr, DeleteTextureFn));
     frames_in_flight_.increment();
@@ -84,10 +75,10 @@
 class AbstractWin32VideoDecoderImpl : public AbstractWin32VideoDecoder,
                                     public MediaBufferConsumerInterface {
  public:
-  explicit AbstractWin32VideoDecoderImpl(SbMediaVideoCodec codec)
+  AbstractWin32VideoDecoderImpl(SbMediaVideoCodec codec, SbDrmSystem drm_system)
       : codec_(codec), surface_width_(0), surface_height_(0) {
     MediaBufferConsumerInterface* media_cb = this;
-    impl_.reset(new DecoderImpl("video", media_cb));
+    impl_.reset(new DecoderImpl("video", media_cb, drm_system));
     EnsureVideoDecoderCreated();
   }
 
@@ -95,10 +86,32 @@
                        int64_t win32_timestamp) {
     const SbMediaTime media_timestamp = ConvertToMediaTime(win32_timestamp);
     VideoFramePtr frame_output =
-        VideoFrameFactory::Construct(media_timestamp, media_buffer);
+        VideoFrameFactory::Construct(media_timestamp, media_buffer,
+            surface_width_, surface_height_);
     output_queue_.PushBack(frame_output);
   }
 
+  void OnNewOutputType(const ComPtr<IMFMediaType>& type) override {
+    MFVideoArea aperture;
+    HRESULT hr = type->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
+        reinterpret_cast<UINT8*>(&aperture), sizeof(MFVideoArea), nullptr);
+    if (SUCCEEDED(hr)) {
+      // TODO: consider offset as well
+      surface_width_ = aperture.Area.cx;
+      surface_height_ = aperture.Area.cy;
+      return;
+    }
+
+    uint32_t width;
+    uint32_t height;
+    hr = MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE,
+                            &width, &height);
+    if (SUCCEEDED(hr)) {
+      surface_width_ = width;
+      surface_height_ = height;
+    }
+  }
+
   ComPtr<IMFMediaType> Configure(IMFTransform* decoder) {
     ComPtr<IMFMediaType> input_type;
     HRESULT hr = MFCreateMediaType(&input_type);
@@ -160,8 +173,6 @@
     SB_DCHECK(1 == input_stream_count);
     SB_DCHECK(1 == output_stream_count);
 
-    impl_->ActivateDecryptor(media_type);
-
     ComPtr<IMFMediaType> output_type =
         FindMediaType(MFVideoFormat_YV12, decoder.Get());
 
@@ -174,6 +185,10 @@
     hr = decoder->GetAttributes(attributes.GetAddressOf());
     CheckResult(hr);
 
+    hr = attributes->SetUINT32(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT,
+                               kMinimumOutputSampleCount);
+    CheckResult(hr);
+
     UINT32 value = 0;
     hr = attributes->GetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &value);
     SB_DCHECK(hr == S_OK || hr == MF_E_ATTRIBUTENOTFOUND);
@@ -225,19 +240,35 @@
       return false;  // Wait for more data.
     }
 
-    // This would be used for decrypting content.
-    // For now this is empty.
     std::vector<uint8_t> key_id;
     std::vector<uint8_t> iv;
+    std::vector<Subsample> subsamples;
 
-    std::vector<Subsample> empty_subsample;
+    // TODO: Merge this with similar code in the audio decoder.
+    const SbDrmSampleInfo* drm_info = buff->drm_info();
+
+    if (drm_info != NULL && drm_info->initialization_vector_size != 0) {
+      key_id.assign(drm_info->identifier,
+                    drm_info->identifier + drm_info->identifier_size);
+      iv.assign(drm_info->initialization_vector,
+                drm_info->initialization_vector +
+                    drm_info->initialization_vector_size);
+      subsamples.reserve(drm_info->subsample_count);
+      for (int32_t i = 0; i < drm_info->subsample_count; ++i) {
+        Subsample subsample = {
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].clear_byte_count),
+            static_cast<uint32_t>(
+                drm_info->subsample_mapping[i].encrypted_byte_count)};
+        subsamples.push_back(subsample);
+      }
+    }
 
     const SbMediaTime media_timestamp = buff->pts();
     const int64_t win32_timestamp = ConvertToWin32Time(media_timestamp);
 
     const bool write_ok = impl_->TryWriteInputBuffer(
-        buff->data(), buff->size(), win32_timestamp,
-        key_id, iv, empty_subsample);
+        buff->data(), buff->size(), win32_timestamp, key_id, iv, subsamples);
 
     return write_ok;
   }
@@ -271,12 +302,14 @@
   uint32_t surface_height_;
   HardwareDecoderContext dx_decoder_ctx_;
 };
+
 }  // anonymous namespace.
 
 scoped_ptr<AbstractWin32VideoDecoder> AbstractWin32VideoDecoder::Create(
-    SbMediaVideoCodec codec) {
+    SbMediaVideoCodec codec,
+    SbDrmSystem drm_system) {
   return scoped_ptr<AbstractWin32VideoDecoder>(
-      new AbstractWin32VideoDecoderImpl(codec));
+      new AbstractWin32VideoDecoderImpl(codec, drm_system));
 }
 
 }  // namespace win32
diff --git a/src/starboard/shared/win32/win32_video_decoder.h b/src/starboard/shared/win32/win32_video_decoder.h
index 84ff7b7..a32158b 100644
--- a/src/starboard/shared/win32/win32_video_decoder.h
+++ b/src/starboard/shared/win32/win32_video_decoder.h
@@ -20,6 +20,7 @@
 
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
@@ -33,7 +34,8 @@
 // VideoDecoder for Win32.
 class AbstractWin32VideoDecoder {
  public:
-  static scoped_ptr<AbstractWin32VideoDecoder> Create(SbMediaVideoCodec codec);
+  static scoped_ptr<AbstractWin32VideoDecoder> Create(SbMediaVideoCodec codec,
+                                                      SbDrmSystem drm_system);
   virtual ~AbstractWin32VideoDecoder() {}
 
   virtual bool TryWrite(const scoped_refptr<InputBuffer>& buff) = 0;
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 19fb1f2..c4a5cb6 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -43,6 +43,9 @@
 namespace starboard {
 namespace shared {
 namespace x11 {
+
+using ::starboard::shared::dev_input::DevInput;
+
 namespace {
 
 enum {
@@ -647,28 +650,6 @@
 }
 #endif
 
-bool XNextEventTimed(Display* display, XEvent* out_event, SbTime duration) {
-  if (XPending(display) == 0) {
-    if (duration <= SbTime()) {
-      return false;
-    }
-
-    int fd = ConnectionNumber(display);
-    fd_set read_set;
-    FD_ZERO(&read_set);
-    FD_SET(fd, &read_set);
-    struct timeval tv;
-    SbTime clamped_duration = std::max(duration, SbTime());
-    ToTimevalDuration(clamped_duration, &tv);
-    if (select(fd + 1, &read_set, NULL, NULL, &tv) == 0) {
-      return false;
-    }
-  }
-
-  XNextEvent(display, out_event);
-  return true;
-}
-
 void XSendAtom(Window window, Atom atom) {
   // XLib is not thread-safe. Since we may be coming from another thread, we
   // have to open another connection to the display to inject the wake-up event.
@@ -732,6 +713,10 @@
 
   SbWindow window = new SbWindowPrivate(display_, options);
   windows_.push_back(window);
+  if (!dev_input_) {
+    // evdev input will be sent to the first created window only.
+    dev_input_.reset(DevInput::Create(window, ConnectionNumber(display_)));
+  }
   return window;
 }
 
@@ -745,6 +730,12 @@
       std::find(windows_.begin(), windows_.end(), window);
   SB_DCHECK(iterator != windows_.end());
   windows_.erase(iterator);
+
+  if (windows_.empty()) {
+    SB_DCHECK(dev_input_);
+    dev_input_.reset();
+  }
+
   delete window;
   if (windows_.empty()) {
     StopX();
@@ -849,23 +840,29 @@
 ApplicationX11::WaitForSystemEventWithTimeout(SbTime time) {
   SB_DCHECK(display_);
 
-  XEvent x_event;
-
   shared::starboard::Application::Event* pending_event = GetPendingEvent();
   if (pending_event) {
     return pending_event;
   }
 
-  if (XNextEventTimed(display_, &x_event, time)) {
+  SB_DCHECK(dev_input_);
+  shared::starboard::Application::Event* evdev_event =
+      dev_input_->WaitForSystemEventWithTimeout(time);
+
+  if (!evdev_event && XPending(display_) != 0) {
+    XEvent x_event;
+    XNextEvent(display_, &x_event);
     return XEventToEvent(&x_event);
-  } else {
-    return NULL;
   }
+
+  return evdev_event;
 }
 
 void ApplicationX11::WakeSystemEventWait() {
   SB_DCHECK(!windows_.empty());
   XSendAtom((*windows_.begin())->window, wake_up_atom_);
+  SB_DCHECK(dev_input_);
+  dev_input_->WakeSystemEventWait();
 }
 
 bool ApplicationX11::EnsureX() {
@@ -879,7 +876,7 @@
   XSetErrorHandler(ErrorHandler);
   display_ = XOpenDisplay(NULL);
   if (!display_) {
-    const char *display_environment = getenv("DISPLAY");
+    const char* display_environment = getenv("DISPLAY");
     if (display_environment == NULL) {
       SB_LOG(ERROR) << "Unable to open display, DISPLAY not set.";
     } else {
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index a0235b1..cadfc90 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -20,9 +20,11 @@
 #include <queue>
 #include <vector>
 
+#include "base/memory/scoped_ptr.h"
 #include "starboard/configuration.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/linux/dev_input/dev_input.h"
 #include "starboard/shared/starboard/application.h"
 #include "starboard/shared/starboard/queue_application.h"
 #include "starboard/types.h"
@@ -117,6 +119,9 @@
   // Indicates whether a key press event that requires a matching release has
   // been dispatched.
   bool paste_buffer_key_release_pending_;
+
+  // The /dev/input input handler. Only set when there is an open window.
+  scoped_ptr<::starboard::shared::dev_input::DevInput> dev_input_;
 };
 
 }  // namespace x11
diff --git a/src/starboard/storage.h b/src/starboard/storage.h
index 37ccc10..33ba547 100644
--- a/src/starboard/storage.h
+++ b/src/starboard/storage.h
@@ -51,13 +51,34 @@
   return record != kSbStorageInvalidRecord;
 }
 
-// Opens and returns a SbStorageRecord for |user|, blocking I/O on the calling
-// thread until it returns. If |user| is not a valid |SbUser|, the function
-// returns |kSbStorageInvalidRecord|.
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Opens and returns the default SbStorageRecord for |user|, blocking I/O on the
+// calling thread until the open is completed. If |user| is not a valid
+// |SbUser|, the function returns |kSbStorageInvalidRecord|. Will return an
+// |SbStorageRecord| of size zero if the record does not yet exist. Opening an
+// already-open |SbStorageRecord| has undefined behavior.
 //
-// |user|: The user for which the storage record is opened.
+// |user|: The user for which the storage record will be opened.
 SB_EXPORT SbStorageRecord SbStorageOpenRecord(SbUser user);
 
+#else  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Opens and returns the SbStorageRecord for |user| named |name|, blocking I/O
+// on the calling thread until the open is completed. If |user| is not a valid
+// |SbUser|, the function returns |kSbStorageInvalidRecord|. Will return an
+// |SbStorageRecord| of size zero if the record does not yet exist. Opening an
+// already-open |SbStorageRecord| has undefined behavior.
+//
+// If |name| is NULL, opens the default storage record for the user, like what
+// would have been saved with the previous version of SbStorageOpenRecord.
+//
+// |user|: The user for which the storage record will be opened.
+// |name|: The filesystem-safe name of the record to open.
+SB_EXPORT SbStorageRecord SbStorageOpenRecord(SbUser user, const char* name);
+
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
 // Closes |record|, synchronously ensuring that all written data is flushed.
 // This function performs blocking I/O on the calling thread.
 //
@@ -91,9 +112,15 @@
 
 // Replaces the data in |record| with |data_size| bytes from |data|. This
 // function always deletes any previous data in that record. The return value
-// indicates whether the write succeeded. This function makes a best-effort
-// to read the entire record, and it performs performs blocking I/O on the
-// calling thread until the entire record is read or an error is encountered.
+// indicates whether the write succeeded. This function makes a best-effort to
+// write the entire record, and it may perform blocking I/O on the calling
+// thread until the entire record is written or an error is encountered.
+//
+// While |SbStorageWriteRecord()| may defer the persistence,
+// |SbStorageReadRecord()| is expected to work as expected immediately
+// afterwards, even without a call to |SbStorageCloseRecord()|. The data should
+// be persisted after a short time, even if there is an unexpected process
+// termination before |SbStorageCloseRecord()| is called.
 //
 // |record|: The record to be written to.
 // |data|: The data to write to the record.
@@ -104,21 +131,43 @@
                                     const char* data,
                                     int64_t data_size);
 
-// Deletes the |SbStorageRecord| for the specified user. The return value
+#if SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Deletes the default |SbStorageRecord| for the |user|. The return value
 // indicates whether the record existed and was successfully deleted. If the
 // record did not exist or could not be deleted, the function returns |false|.
 //
 // This function must not be called while the user's storage record is open.
 // This function performs blocking I/O on the calling thread.
 //
-// |user|: The user for whom the record is deleted.
+// |user|: The user for whom the record will be deleted.
 SB_EXPORT bool SbStorageDeleteRecord(SbUser user);
 
+#else  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
+// Deletes the |SbStorageRecord| for |user| named |name|. The return value
+// indicates whether the record existed and was successfully deleted. If the
+// record did not exist or could not be deleted, the function returns |false|.
+//
+// If |name| is NULL, deletes the default storage record for the user, like what
+// would have been deleted with the previous version of SbStorageDeleteRecord.
+//
+// This function must not be called while the user's storage record is open.
+// This function performs blocking I/O on the calling thread.
+//
+// |user|: The user for whom the record will be deleted.
+// |name|: The filesystem-safe name of the record to open.
+SB_EXPORT bool SbStorageDeleteRecord(SbUser user, const char* name);
+
+#endif  // SB_API_VERSION < SB_STORAGE_NAMES_API_VERSION
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
 
 #ifdef __cplusplus
+#include <string>
+
 namespace starboard {
 
 // Inline scoped wrapper for SbStorageRecord.
@@ -126,18 +175,28 @@
  public:
   StorageRecord()
       : user_(SbUserGetCurrent()), record_(kSbStorageInvalidRecord) {
-    if (SbUserIsValid(user_)) {
-      record_ = SbStorageOpenRecord(user_);
-    }
+    Initialize();
   }
 
   explicit StorageRecord(SbUser user)
       : user_(user), record_(kSbStorageInvalidRecord) {
-    if (SbUserIsValid(user_)) {
-      record_ = SbStorageOpenRecord(user_);
-    }
+    Initialize();
   }
 
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  explicit StorageRecord(const char* name)
+      : user_(SbUserGetCurrent()),
+        name_(name),
+        record_(kSbStorageInvalidRecord) {
+    Initialize();
+  }
+
+  StorageRecord(SbUser user, const char* name)
+      : user_(user), name_(name), record_(kSbStorageInvalidRecord) {
+    Initialize();
+  }
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+
   ~StorageRecord() { Close(); }
   bool IsValid() { return SbStorageIsValidRecord(record_); }
   int64_t GetSize() { return SbStorageGetRecordSize(record_); }
@@ -160,11 +219,36 @@
 
   bool Delete() {
     Close();
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+    if (!name_.empty()) {
+      return SbStorageDeleteRecord(user_, name_.c_str());
+    } else {
+      return SbStorageDeleteRecord(user_, NULL);
+    }
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
     return SbStorageDeleteRecord(user_);
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   }
 
  private:
+  void Initialize() {
+    if (SbUserIsValid(user_)) {
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+      if (!name_.empty()) {
+        record_ = SbStorageOpenRecord(user_, name_.c_str());
+      } else {
+        record_ = SbStorageOpenRecord(user_, NULL);
+      }
+#else   // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+      record_ = SbStorageOpenRecord(user_);
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+    }
+  }
+
   SbUser user_;
+#if SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
+  std::string name_;
+#endif  // SB_API_VERSION >= SB_STORAGE_NAMES_API_VERSION
   SbStorageRecord record_;
 };
 
diff --git a/src/starboard/stub/BUILD.gn b/src/starboard/stub/BUILD.gn
new file mode 100644
index 0000000..18ad876
--- /dev/null
+++ b/src/starboard/stub/BUILD.gn
@@ -0,0 +1,428 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/fastbuild.gni")
+import("//starboard/build/delegated_config.gni")
+
+# =============================================================================
+# DEFAULT COMPILER CONFIGS
+# =============================================================================
+
+config("compiler_defaults") {
+  cflags = [
+    # We'll pretend not to be Linux, but Starboard instead.
+    "-U__linux__",
+    "-Werror",
+    "-fcolor-diagnostics",
+    # Default visibility to hidden, to enable dead stripping.
+    "-fvisibility=hidden",
+    # Warn for implicit type conversions that may change a value.
+    "-Wconversion",
+    "-Wno-c++11-compat",
+    # This (rightfully) 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",
+    # 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
+    # TODO(pkasting): In C++11 this is legal, so this should be removed when we
+    # change to that.  (This is also why we don't bother fixing all these cases
+    # today.)
+    "-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",
+  ]
+
+  defines = [
+    "__STDC_FORMAT_MACROS",
+  ]
+
+  cflags_cc = [
+    "-std=gnu++11",
+  ]
+}
+
+config("compiler_defaults_debug") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_devel") {
+  cflags = []
+  if (!cobalt_use_fastbuild) {
+    cflags += [ "-g" ]
+  }
+}
+
+config("compiler_defaults_qa") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+config("compiler_defaults_gold") {
+  cflags = [
+    "-gline-tables-only",
+  ]
+}
+
+# =============================================================================
+# DELEGATED CONFIGS
+# =============================================================================
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+  ]
+}
+
+config("no_pedantic_warnings") {
+  cflags = [
+    # "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 about unused function params.
+    "-Wno-unused-parameter",
+    # 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",
+  ]
+}
+
+
+delegated_config("optimizations") {
+  path = "//starboard/build/toolchain/linux/config"
+  prefixes = [ "no", "debuggable", "full" ]
+  generate_default = false
+}
+
+config("default_optimizations") {
+  if (cobalt_config == "debug") {
+    configs = [ ":no_optimizations" ]
+  } else {
+    configs = [ ":debuggable_optimizations" ]
+  }
+}
+
+
+delegated_config("rtti") {
+  path = "//starboard/build/toolchain/linux/config"
+  generate_default = false
+}
+
+config("default_rtti") {
+  if (cobalt_config == "debug" || cobalt_config == "devel") {
+    configs = [ ":rtti" ]
+  } else {
+    configs = [ ":no_rtti" ]
+  }
+}
+
+
+config("wexit_time_destructors") {
+  configs = [ "//starboard/build/toolchain/linux/config:wexit_time_destructors" ]
+}
+
+# =============================================================================
+# starboard_platform TARGET
+# =============================================================================
+
+static_library("starboard_platform") {
+  sources = [
+    "//starboard/shared/starboard/application.cc",
+    "//starboard/shared/starboard/command_line.cc",
+    "//starboard/shared/starboard/command_line.h",
+    "//starboard/shared/starboard/event_cancel.cc",
+    "//starboard/shared/starboard/event_schedule.cc",
+    "//starboard/shared/starboard/file_mode_string_to_flags.cc",
+    "//starboard/shared/starboard/log_message.cc",
+    "//starboard/shared/starboard/player/filter/stub_player_components_impl.cc",
+    "//starboard/shared/starboard/queue_application.cc",
+    "//starboard/shared/stub/accessibility_get_display_settings.cc",
+    "//starboard/shared/stub/accessibility_get_text_to_speech_settings.cc",
+    "//starboard/shared/stub/atomic_public.h",
+    "//starboard/shared/stub/audio_sink_create.cc",
+    "//starboard/shared/stub/audio_sink_destroy.cc",
+    "//starboard/shared/stub/audio_sink_get_max_channels.cc",
+    "//starboard/shared/stub/audio_sink_get_nearest_supported_sample_frequency.cc",
+    "//starboard/shared/stub/audio_sink_is_audio_frame_storage_type_supported.cc",
+    "//starboard/shared/stub/audio_sink_is_audio_sample_type_supported.cc",
+    "//starboard/shared/stub/audio_sink_is_valid.cc",
+    "//starboard/shared/stub/byte_swap.cc",
+    "//starboard/shared/stub/character_is_alphanumeric.cc",
+    "//starboard/shared/stub/character_is_digit.cc",
+    "//starboard/shared/stub/character_is_hex_digit.cc",
+    "//starboard/shared/stub/character_is_space.cc",
+    "//starboard/shared/stub/character_is_upper.cc",
+    "//starboard/shared/stub/character_to_lower.cc",
+    "//starboard/shared/stub/character_to_upper.cc",
+    "//starboard/shared/stub/condition_variable_broadcast.cc",
+    "//starboard/shared/stub/condition_variable_create.cc",
+    "//starboard/shared/stub/condition_variable_destroy.cc",
+    "//starboard/shared/stub/condition_variable_signal.cc",
+    "//starboard/shared/stub/condition_variable_wait.cc",
+    "//starboard/shared/stub/condition_variable_wait_timed.cc",
+    "//starboard/shared/stub/cryptography_create_transformer.cc",
+    "//starboard/shared/stub/cryptography_destroy_transformer.cc",
+    "//starboard/shared/stub/cryptography_get_tag.cc",
+    "//starboard/shared/stub/cryptography_set_authenticated_data.cc",
+    "//starboard/shared/stub/cryptography_set_initialization_vector.cc",
+    "//starboard/shared/stub/cryptography_transform.cc",
+    "//starboard/shared/stub/directory_can_open.cc",
+    "//starboard/shared/stub/directory_close.cc",
+    "//starboard/shared/stub/directory_create.cc",
+    "//starboard/shared/stub/directory_get_next.cc",
+    "//starboard/shared/stub/directory_open.cc",
+    "//starboard/shared/stub/double_absolute.cc",
+    "//starboard/shared/stub/double_exponent.cc",
+    "//starboard/shared/stub/double_floor.cc",
+    "//starboard/shared/stub/double_is_finite.cc",
+    "//starboard/shared/stub/double_is_nan.cc",
+    "//starboard/shared/stub/drm_close_session.cc",
+    "//starboard/shared/stub/drm_create_system.cc",
+    "//starboard/shared/stub/drm_destroy_system.cc",
+    "//starboard/shared/stub/drm_generate_session_update_request.cc",
+    "//starboard/shared/stub/drm_system_internal.h",
+    "//starboard/shared/stub/drm_update_session.cc",
+    "//starboard/shared/stub/file_can_open.cc",
+    "//starboard/shared/stub/file_close.cc",
+    "//starboard/shared/stub/file_delete.cc",
+    "//starboard/shared/stub/file_exists.cc",
+    "//starboard/shared/stub/file_flush.cc",
+    "//starboard/shared/stub/file_get_info.cc",
+    "//starboard/shared/stub/file_get_path_info.cc",
+    "//starboard/shared/stub/file_open.cc",
+    "//starboard/shared/stub/file_read.cc",
+    "//starboard/shared/stub/file_seek.cc",
+    "//starboard/shared/stub/file_truncate.cc",
+    "//starboard/shared/stub/file_write.cc",
+    "//starboard/shared/stub/log.cc",
+    "//starboard/shared/stub/log_flush.cc",
+    "//starboard/shared/stub/log_format.cc",
+    "//starboard/shared/stub/log_is_tty.cc",
+    "//starboard/shared/stub/log_raw.cc",
+    "//starboard/shared/stub/log_raw_dump_stack.cc",
+    "//starboard/shared/stub/log_raw_format.cc",
+    "//starboard/shared/stub/media_can_play_mime_and_key_system.cc",
+    "//starboard/shared/stub/media_get_audio_configuration.cc",
+    "//starboard/shared/stub/media_get_audio_output_count.cc",
+    "//starboard/shared/stub/media_is_audio_supported.cc",
+    "//starboard/shared/stub/media_is_output_protected.cc",
+    "//starboard/shared/stub/media_is_supported.cc",
+    "//starboard/shared/stub/media_is_transfer_characteristics_supported.cc",
+    "//starboard/shared/stub/media_is_video_supported.cc",
+    "//starboard/shared/stub/media_set_output_protection.cc",
+    "//starboard/shared/stub/memory_allocate_aligned_unchecked.cc",
+    "//starboard/shared/stub/memory_allocate_unchecked.cc",
+    "//starboard/shared/stub/memory_compare.cc",
+    "//starboard/shared/stub/memory_copy.cc",
+    "//starboard/shared/stub/memory_find_byte.cc",
+    "//starboard/shared/stub/memory_flush.cc",
+    "//starboard/shared/stub/memory_free.cc",
+    "//starboard/shared/stub/memory_free_aligned.cc",
+    "//starboard/shared/stub/memory_get_stack_bounds.cc",
+    "//starboard/shared/stub/memory_map.cc",
+    "//starboard/shared/stub/memory_move.cc",
+    "//starboard/shared/stub/memory_reallocate_unchecked.cc",
+    "//starboard/shared/stub/memory_set.cc",
+    "//starboard/shared/stub/memory_unmap.cc",
+    "//starboard/shared/stub/microphone_close.cc",
+    "//starboard/shared/stub/microphone_create.cc",
+    "//starboard/shared/stub/microphone_destroy.cc",
+    "//starboard/shared/stub/microphone_get_available.cc",
+    "//starboard/shared/stub/microphone_is_sample_rate_supported.cc",
+    "//starboard/shared/stub/microphone_open.cc",
+    "//starboard/shared/stub/microphone_read.cc",
+    "//starboard/shared/stub/mutex_acquire.cc",
+    "//starboard/shared/stub/mutex_acquire_try.cc",
+    "//starboard/shared/stub/mutex_create.cc",
+    "//starboard/shared/stub/mutex_destroy.cc",
+    "//starboard/shared/stub/mutex_release.cc",
+    "//starboard/shared/stub/once.cc",
+    "//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_output_mode_supported.cc",
+    "//starboard/shared/stub/player_seek.cc",
+    "//starboard/shared/stub/player_set_bounds.cc",
+    "//starboard/shared/stub/player_set_pause.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",
+    "//starboard/shared/stub/socket_connect.cc",
+    "//starboard/shared/stub/socket_create.cc",
+    "//starboard/shared/stub/socket_destroy.cc",
+    "//starboard/shared/stub/socket_free_resolution.cc",
+    "//starboard/shared/stub/socket_get_interface_address.cc",
+    "//starboard/shared/stub/socket_get_last_error.cc",
+    "//starboard/shared/stub/socket_get_local_address.cc",
+    "//starboard/shared/stub/socket_get_local_interface_address.cc",
+    "//starboard/shared/stub/socket_is_connected.cc",
+    "//starboard/shared/stub/socket_is_connected_and_idle.cc",
+    "//starboard/shared/stub/socket_join_multicast_group.cc",
+    "//starboard/shared/stub/socket_listen.cc",
+    "//starboard/shared/stub/socket_receive_from.cc",
+    "//starboard/shared/stub/socket_resolve.cc",
+    "//starboard/shared/stub/socket_send_to.cc",
+    "//starboard/shared/stub/socket_set_broadcast.cc",
+    "//starboard/shared/stub/socket_set_receive_buffer_size.cc",
+    "//starboard/shared/stub/socket_set_reuse_address.cc",
+    "//starboard/shared/stub/socket_set_send_buffer_size.cc",
+    "//starboard/shared/stub/socket_set_tcp_keep_alive.cc",
+    "//starboard/shared/stub/socket_set_tcp_no_delay.cc",
+    "//starboard/shared/stub/socket_set_tcp_window_scaling.cc",
+    "//starboard/shared/stub/socket_waiter_add.cc",
+    "//starboard/shared/stub/socket_waiter_create.cc",
+    "//starboard/shared/stub/socket_waiter_destroy.cc",
+    "//starboard/shared/stub/socket_waiter_remove.cc",
+    "//starboard/shared/stub/socket_waiter_wait.cc",
+    "//starboard/shared/stub/socket_waiter_wait_timed.cc",
+    "//starboard/shared/stub/socket_waiter_wake_up.cc",
+    "//starboard/shared/stub/speech_recognizer_cancel.cc",
+    "//starboard/shared/stub/speech_recognizer_create.cc",
+    "//starboard/shared/stub/speech_recognizer_destroy.cc",
+    "//starboard/shared/stub/speech_recognizer_start.cc",
+    "//starboard/shared/stub/speech_recognizer_stop.cc",
+    "//starboard/shared/stub/speech_synthesis_cancel.cc",
+    "//starboard/shared/stub/speech_synthesis_set_language.cc",
+    "//starboard/shared/stub/speech_synthesis_speak.cc",
+    "//starboard/shared/stub/storage_close_record.cc",
+    "//starboard/shared/stub/storage_delete_record.cc",
+    "//starboard/shared/stub/storage_get_record_size.cc",
+    "//starboard/shared/stub/storage_open_record.cc",
+    "//starboard/shared/stub/storage_read_record.cc",
+    "//starboard/shared/stub/storage_write_record.cc",
+    "//starboard/shared/stub/string_compare.cc",
+    "//starboard/shared/stub/string_compare_all.cc",
+    "//starboard/shared/stub/string_compare_no_case.cc",
+    "//starboard/shared/stub/string_compare_no_case_n.cc",
+    "//starboard/shared/stub/string_compare_wide.cc",
+    "//starboard/shared/stub/string_concat.cc",
+    "//starboard/shared/stub/string_concat_wide.cc",
+    "//starboard/shared/stub/string_copy.cc",
+    "//starboard/shared/stub/string_copy_wide.cc",
+    "//starboard/shared/stub/string_duplicate.cc",
+    "//starboard/shared/stub/string_find_character.cc",
+    "//starboard/shared/stub/string_find_last_character.cc",
+    "//starboard/shared/stub/string_find_string.cc",
+    "//starboard/shared/stub/string_format.cc",
+    "//starboard/shared/stub/string_format_wide.cc",
+    "//starboard/shared/stub/string_get_length.cc",
+    "//starboard/shared/stub/string_get_length_wide.cc",
+    "//starboard/shared/stub/string_parse_double.cc",
+    "//starboard/shared/stub/string_parse_signed_integer.cc",
+    "//starboard/shared/stub/string_parse_uint64.cc",
+    "//starboard/shared/stub/string_parse_unsigned_integer.cc",
+    "//starboard/shared/stub/string_scan.cc",
+    "//starboard/shared/stub/system_binary_search.cc",
+    "//starboard/shared/stub/system_break_into_debugger.cc",
+    "//starboard/shared/stub/system_clear_last_error.cc",
+    "//starboard/shared/stub/system_clear_platform_error.cc",
+    "//starboard/shared/stub/system_get_connection_type.cc",
+    "//starboard/shared/stub/system_get_device_type.cc",
+    "//starboard/shared/stub/system_get_error_string.cc",
+    "//starboard/shared/stub/system_get_last_error.cc",
+    "//starboard/shared/stub/system_get_locale_id.cc",
+    "//starboard/shared/stub/system_get_number_of_processors.cc",
+    "//starboard/shared/stub/system_get_path.cc",
+    "//starboard/shared/stub/system_get_property.cc",
+    "//starboard/shared/stub/system_get_random_data.cc",
+    "//starboard/shared/stub/system_get_random_uint64.cc",
+    "//starboard/shared/stub/system_get_stack.cc",
+    "//starboard/shared/stub/system_get_total_cpu_memory.cc",
+    "//starboard/shared/stub/system_get_total_gpu_memory.cc",
+    "//starboard/shared/stub/system_get_used_cpu_memory.cc",
+    "//starboard/shared/stub/system_get_used_gpu_memory.cc",
+    "//starboard/shared/stub/system_has_capability.cc",
+    "//starboard/shared/stub/system_hide_splash_screen.cc",
+    "//starboard/shared/stub/system_is_debugger_attached.cc",
+    "//starboard/shared/stub/system_raise_platform_error.cc",
+    "//starboard/shared/stub/system_request_pause.cc",
+    "//starboard/shared/stub/system_request_stop.cc",
+    "//starboard/shared/stub/system_request_suspend.cc",
+    "//starboard/shared/stub/system_request_unpause.cc",
+    "//starboard/shared/stub/system_sort.cc",
+    "//starboard/shared/stub/system_symbolize.cc",
+    "//starboard/shared/stub/thread_create.cc",
+    "//starboard/shared/stub/thread_create_local_key.cc",
+    "//starboard/shared/stub/thread_destroy_local_key.cc",
+    "//starboard/shared/stub/thread_detach.cc",
+    "//starboard/shared/stub/thread_get_current.cc",
+    "//starboard/shared/stub/thread_get_id.cc",
+    "//starboard/shared/stub/thread_get_local_value.cc",
+    "//starboard/shared/stub/thread_get_name.cc",
+    "//starboard/shared/stub/thread_is_equal.cc",
+    "//starboard/shared/stub/thread_join.cc",
+    "//starboard/shared/stub/thread_set_local_value.cc",
+    "//starboard/shared/stub/thread_set_name.cc",
+    "//starboard/shared/stub/thread_sleep.cc",
+    "//starboard/shared/stub/thread_types_public.h",
+    "//starboard/shared/stub/thread_yield.cc",
+    "//starboard/shared/stub/time_get_monotonic_now.cc",
+    "//starboard/shared/stub/time_get_monotonic_thread_now.cc",
+    "//starboard/shared/stub/time_get_now.cc",
+    "//starboard/shared/stub/time_zone_get_current.cc",
+    "//starboard/shared/stub/time_zone_get_dst_name.cc",
+    "//starboard/shared/stub/time_zone_get_name.cc",
+    "//starboard/shared/stub/user_get_current.cc",
+    "//starboard/shared/stub/user_get_property.cc",
+    "//starboard/shared/stub/user_get_signed_in.cc",
+    "//starboard/shared/stub/window_create.cc",
+    "//starboard/shared/stub/window_destroy.cc",
+    "//starboard/shared/stub/window_get_platform_handle.cc",
+    "//starboard/shared/stub/window_get_size.cc",
+    "//starboard/shared/stub/window_set_default_options.cc",
+    "application_stub.cc",
+    "application_stub.h",
+    "atomic_public.h",
+    "main.cc",
+    "thread_types_public.h",
+  ]
+
+  # Include private stubs, if present.
+  private_sources = exec_script("//starboard/tools/find_private_files.py",
+                                [
+                                  rebase_path("//", root_build_dir),
+                                  "shared/stub/*.cc",
+                                ],
+                                "list lines",
+                                [ "//starboard/private/shared/stub" ])
+  sources += rebase_path(private_sources, ".", root_build_dir)
+
+  defines = [ "STARBOARD_IMPLEMENTATION" ]
+}
diff --git a/src/starboard/stub/buildconfig.gni b/src/starboard/stub/buildconfig.gni
new file mode 100644
index 0000000..8e4bdee
--- /dev/null
+++ b/src/starboard/stub/buildconfig.gni
@@ -0,0 +1,36 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+###############################################################################
+# This file is imported into BUILDCONFIG.gn. As a side effect, any variable
+# defined here becomes a global variable in all GN files.
+#
+# Adding new variables to this file could result in variable name conflicts, so
+# keep the number of variables in this file to a minimum!
+#
+# If you need to define a temporary variable, prefix it with an underscore like
+# _this. Prefixing a variable with an underscore makes it a private variable
+# that won't bleed into other files.
+###############################################################################
+
+# Target OS and CPU.
+# BUILDCONFIG.gn sets target_os and target_cpu to the values of these two
+# variables below. Unfortunately, due to GN's restrictions on how variables can
+# be changed, we can't directly set target_os and target_cpu here.
+target_os_ = "linux"
+target_cpu_ = "x64"
+
+# The target and host toolchain
+target_toolchain = "//starboard/build/toolchain/linux:clang_x64"
+host_toolchain = "//starboard/build/toolchain/linux:clang_$host_cpu"
diff --git a/src/starboard/stub/configuration.gni b/src/starboard/stub/configuration.gni
new file mode 100644
index 0000000..55a886e
--- /dev/null
+++ b/src/starboard/stub/configuration.gni
@@ -0,0 +1,29 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Use a stub rasterizer and graphical setup
+rasterizer_type = "stub"
+
+# No GL drivers available
+gl_type = "none"
+
+# Use media source extension implementation that is conformed to the
+# Candidate Recommandation of July 5th 2016.
+cobalt_use_media_source_2016 = true
+
+declare_args() {
+  # Set to true to enable distributed compilation using Goma. By default we
+  # use Goma for stub and linux.
+  use_goma = true
+}
diff --git a/src/starboard/tools/abstract_launcher.py b/src/starboard/tools/abstract_launcher.py
index 65bc1ea..a840cde 100644
--- a/src/starboard/tools/abstract_launcher.py
+++ b/src/starboard/tools/abstract_launcher.py
@@ -1,4 +1,5 @@
-#!/usr/bin/python
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -10,59 +11,25 @@
 # 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."""
+# limitations under the License.
 """Abstraction for running Cobalt development tools."""
 
-import abc
-import imp
+import importlib
 import os
-import re
 import sys
 
-platform_module = imp.load_source(
-    "platform", os.path.abspath(
-        os.path.join(os.path.dirname(__file__), "platform.py")))
+if "environment" in sys.modules:
+  environment = sys.modules["environment"]
+else:
+  env_path = os.path.abspath(os.path.dirname(__file__))
+  if env_path not in sys.path:
+    sys.path.append(env_path)
+  environment = importlib.import_module("environment")
 
 
-def _GetAllPlatforms(path):
-  """Retrieves information about all available Cobalt ports.
+import abc
 
-  Args:
-    path:  Root path that will be crawled to find ports.
-
-  Returns:
-    Dict mapping available cobalt ports to their respective location in
-    the filesystem.
-  """
-  platform_dict = {}
-  for port in platform_module.PlatformInfo.EnumeratePorts(path):
-    port_name = re.search(".*starboard-(.*)", port.port_name).group(1)
-    platform_dict[port_name] = port.path
-  return platform_dict
-
-
-def _GetProjectRoot():
-  """Gets the root of this project.
-
-  Returns:
-    Path to the root of this project
-
-  Raises:
-    RuntimeError: There is no root.
-  """
-  current_path = os.path.normpath(os.path.dirname(__file__))
-  while not os.access(os.path.join(current_path, ".gclient"), os.R_OK):
-    next_path = os.path.dirname(current_path)
-    if next_path == current_path:
-      current_path = None
-      break
-    current_path = next_path
-  client_root = current_path
-
-  if not client_root:
-    raise RuntimeError("No project root declared.")
-
-  return client_root
+import starboard.tools.platform as platform_module
 
 
 def _GetLauncherForPlatform(platform_path):
@@ -74,23 +41,9 @@
   Returns:
     The module containing the platform's launcher implementation.
   """
-
-  # Necessary because gyp_configuration modules use relative imports.
-  # "cobalt/build" needs to be in sys.path to keep the imports working
-  sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                               os.pardir, os.pardir,
-                                               "cobalt", "build")))
-
-  # Necessary because the gyp_configuration for linux-x64x11 imports
-  # directly from "starboard/".  All of this import logic will eventually be
-  # moved to a configuration system.
-  sys.path.append(os.path.abspath(os.path.join(
-      os.path.dirname(__file__), os.pardir, os.pardir)))
-
-  module_path = os.path.abspath(os.path.join(platform_path,
-                                             "gyp_configuration.py"))
-
-  gyp_module = imp.load_source("platform_module", module_path)
+  if platform_path not in sys.path:
+    sys.path.append(platform_path)
+  gyp_module = importlib.import_module("gyp_configuration")
   return gyp_module.CreatePlatformConfig().GetLauncher()
 
 
@@ -112,8 +65,7 @@
   """
 
   #  Creates launcher for provided platform if the platform has a valid port
-  client_root = _GetProjectRoot()
-  platform_dict = _GetAllPlatforms(client_root)
+  platform_dict = platform_module.GetAllPorts()
   if platform in platform_dict:
     platform_path = platform_dict[platform]
     launcher_module = _GetLauncherForPlatform(platform_path)
@@ -140,7 +92,11 @@
 
   @abc.abstractmethod
   def Run(self):
-    """Runs the launcher's executable.  Must be implemented in subclasses."""
+    """Runs the launcher's executable.  Must be implemented in subclasses.
+
+    Returns:
+      The return code from the launcher's executable.
+    """
     pass
 
   @abc.abstractmethod
diff --git a/src/starboard/tools/environment.py b/src/starboard/tools/environment.py
new file mode 100644
index 0000000..e792803
--- /dev/null
+++ b/src/starboard/tools/environment.py
@@ -0,0 +1,94 @@
+#
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License."""
+"""Sets up a Starboard environment for use by the host application.
+
+Applications that use Starboard should contain a Python file, called
+"starboard_configuration.py", somewhere in the ancestor path of their
+"starboard" directory.
+
+The file should contain a variable, called PORT_ROOTS. Assign to it a
+list of paths to directories in which Starboard ports are located.
+Each path should be represented as a list of directory names whose locations
+are relative to the configuration file.  Example:
+
+PORT_ROOTS = [
+    ["some_directory", "starboard"]
+    ["somewhere_else", "starboard"]
+]
+
+IF YOU ARE GOING TO USE STARBOARD FILES OUTSIDE OF STARBOARD, YOU NEED TO IMPORT
+THIS MODULE FIRST.  Otherwise, sys.path will not be configured properly, none of
+your code will work, and you'll be sad.
+"""
+
+import importlib
+import os
+import sys
+
+PATHS = [
+    # Necessary because the gyp_configuration for linux-x64x11 imports
+    # directly from "starboard/".
+    os.path.abspath(os.path.join(
+        os.path.dirname(__file__), os.pardir, os.pardir)),
+
+    # Necessary because gyp_configuration modules use relative imports.
+    # "cobalt/build" needs to be in sys.path to keep the imports working
+    os.path.abspath(os.path.join(
+        os.path.dirname(__file__), os.pardir, os.pardir,
+        "cobalt", "build"))
+]
+
+# This module should only add the above paths to sys.path when they are not
+# present in the list already.
+for new_path in PATHS:
+  if new_path not in sys.path:
+    sys.path.append(new_path)
+
+
+def _GetStarboardConfigPath():
+  """Gets the path to the "starboard_configuration.py" file from the host app.
+
+  Returns:
+    Path to the "starboard_configuration.py" file from the host app
+
+  Raises:
+    RuntimeError: There is no configuration file.
+  """
+  current_path = os.path.normpath(os.path.dirname(__file__))
+  while not os.path.exists(os.path.join(current_path,
+                                        "starboard_configuration.py")):
+    next_path = os.path.dirname(current_path)
+    if next_path == current_path:
+      current_path = None
+      break
+    current_path = next_path
+  config_path = current_path
+
+  if not config_path:
+    raise RuntimeError("No starboard configuration declared.")
+
+  return config_path
+
+
+def GetStarboardPortRoots():
+  """Gets paths to the host app's Starboard port directories."""
+  config_path = _GetStarboardConfigPath()
+  sys.path.append(config_path)
+  config_module = importlib.import_module("starboard_configuration")
+  port_roots = config_module.PORT_ROOTS
+  port_root_paths = [os.path.abspath(os.path.join(
+      config_path, os.sep.join(root))) for root in port_roots]
+  return port_root_paths
+
diff --git a/src/starboard/tools/platform.py b/src/starboard/tools/platform.py
index 7c760f8..76e3438 100644
--- a/src/starboard/tools/platform.py
+++ b/src/starboard/tools/platform.py
@@ -16,8 +16,19 @@
 """Functionality to enumerate and represent starboard ports."""
 
 import importlib
-import logging
 import os
+import sys
+
+if "environment" in sys.modules:
+  environment = sys.modules["environment"]
+else:
+  env_path = os.path.abspath(os.path.dirname(__file__))
+  if env_path not in sys.path:
+    sys.path.append(env_path)
+  environment = importlib.import_module("environment")
+
+
+import logging
 import re
 
 
@@ -61,11 +72,37 @@
   return re.sub(r'[^a-zA-Z0-9_]', r'-', directory[start:])
 
 
+def _GetAllPlatforms(port_root_paths):
+  """Retrieves information about all available Cobalt ports.
+
+  Args:
+    port_root_paths:  List of paths that will be crawled to find ports.
+
+  Returns:
+    Dict mapping each available port to its location in the filesystem.
+  """
+  platform_dict = {}
+  for path in port_root_paths:
+    for port in PlatformInfo.EnumeratePorts(path):
+      platform_dict[port.port_name] = port.path
+  return platform_dict
+
+
+def GetAllPorts():
+  """Gets all available starboard ports from the host app.
+
+  Returns:
+    Dictionary mapping port names to their path in the filesystem.
+  """
+  port_root_paths = environment.GetStarboardPortRoots()
+  return _GetAllPlatforms(port_root_paths)
+
+
 class PlatformInfo(object):
   """Information about a specific starboard port."""
 
   @classmethod
-  def EnumeratePorts(cls, root_path, exclusion_set = None):
+  def EnumeratePorts(cls, root_path, exclusion_set=None):
     """Generator that iterates over starboard ports found under |path|."""
     if not exclusion_set:
       exclusion_set = set()
diff --git a/src/starboard/win/lib/gyp_configuration.gypi b/src/starboard/win/lib/gyp_configuration.gypi
index 0dec384..e491c37 100644
--- a/src/starboard/win/lib/gyp_configuration.gypi
+++ b/src/starboard/win/lib/gyp_configuration.gypi
@@ -16,6 +16,16 @@
   'variables': {
     'javascript_engine': 'mozjs',
     'cobalt_enable_jit': 0,
+    # TODO: In theory, there are tools that can combine static libraries into
+    # thick static libraries with all their transitive dependencies. Using
+    # shared_library here can have unexpected consequences. Explore building
+    # this into a thick static library instead.
+    'final_executable_type': 'shared_library',
+    'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
+    'sb_enable_lib': 1,
+    'angle_build_winrt': 0,
+    'winrt': 0,
+    'enable_d3d11_feature_level_11': 1,
   },
   'includes': [
     '../shared/gyp_configuration.gypi',
@@ -23,25 +33,18 @@
   'target_defaults': {
     'default_configuration': 'win-lib_debug',
     'configurations': {
-     'lib_base': {
-       'abstract': 1,
-       'msvs_settings': {
-         'VCLinkerTool': {
-           'SubSystem': '2', # WINDOWS
-         }
-       }
-      },
+
       'win-lib_debug': {
-        'inherit_from': ['msvs_debug', 'lib_base'],
+        'inherit_from': ['win32_base', 'msvs_debug'],
       },
       'win-lib_devel': {
-       'inherit_from': ['msvs_devel', 'lib_base'],
+       'inherit_from': ['win32_base', 'msvs_devel'],
       },
       'win-lib_qa': {
-        'inherit_from': ['msvs_qa', 'lib_base'],
+        'inherit_from': ['win32_base', 'msvs_qa'],
       },
       'win-lib_gold': {
-        'inherit_from': ['msvs_gold', 'lib_base'],
+        'inherit_from': ['win32_base', 'msvs_gold'],
       },
     },  # end of configurations
   },
diff --git a/src/starboard/win/lib/main.cc b/src/starboard/win/lib/main.cc
deleted file mode 100644
index 2ab7084..0000000
--- a/src/starboard/win/lib/main.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <windows.h>
-
-#include <WinSock2.h>
-
-#include <string>
-#include <vector>
-
-#include "starboard/configuration.h"
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/shared/win32/thread_private.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-using starboard::shared::win32::wchar_tToUTF8;
-
-// TODO: Share more of this logic with xb1 & win-console?
-int main(Platform::Array<Platform::String ^> ^ args) {
-  if (!IsDebuggerPresent()) {
-    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
-  }
-
-  const int kWinSockVersionMajor = 2;
-  const int kWinSockVersionMinor = 2;
-  WSAData wsaData;
-  int init_result = WSAStartup(
-      MAKEWORD(kWinSockVersionMajor, kWinSockVersionMajor), &wsaData);
-
-  SB_CHECK(init_result == 0);
-  // WSAStartup returns the highest version that is supported up to the version
-  // we request.
-  SB_CHECK(LOBYTE(wsaData.wVersion) == kWinSockVersionMajor &&
-           HIBYTE(wsaData.wVersion) == kWinSockVersionMinor);
-
-  starboard::shared::win32::RegisterMainThread();
-
-  std::vector<std::string> string_args;
-  for (auto it = args->begin(); it != args->end(); ++it) {
-    Platform::String ^ s = *it;
-    string_args.push_back(wchar_tToUTF8(s->Data(), s->Length()));
-  }
-
-  std::vector<const char*> utf8_args;
-  for (auto it = string_args.begin(); it != string_args.end(); ++it) {
-    utf8_args.push_back(it->data());
-  }
-
-  starboard::shared::uwp::ApplicationUwp application;
-  int return_value = application.Run(static_cast<int>(utf8_args.size()),
-                                     const_cast<char**>(utf8_args.data()));
-
-  WSACleanup();
-
-  return return_value;
-}
diff --git a/src/starboard/win/lib/starboard_platform.gyp b/src/starboard/win/lib/starboard_platform.gyp
index d61cc40..04fd716 100644
--- a/src/starboard/win/lib/starboard_platform.gyp
+++ b/src/starboard/win/lib/starboard_platform.gyp
@@ -14,7 +14,6 @@
 {
   'includes': [
     '../shared/starboard_platform.gypi',
-    '../../shared/uwp/starboard_platform.gypi'
   ],
   'variables': {
     'starboard_platform_dependent_files': [
@@ -22,6 +21,21 @@
       'configuration_public.h',
       'thread_types_public.h',
       '../shared/system_get_path.cc',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
+      '<@(uwp_incompatible_win32)',
+      '<@(stub_media_player)'
     ],
-  }
+  },
 }
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi
index 7134bee..43ec50b 100644
--- a/src/starboard/win/shared/starboard_platform.gypi
+++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -17,6 +17,16 @@
     'winrt%': 1,
     'stub_media_player': [
     ],
+
+    'stub_drm_system': [
+      '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+      '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
+    ],
+
     # TODO: Move this and the win32 dependencies below to a shared/win32/starboard_platform.gypi?
     'uwp_incompatible_win32': [
       '<(DEPTH)/starboard/shared/win32/application_win32_key_event.cc',
@@ -27,6 +37,8 @@
       '<(DEPTH)/starboard/shared/win32/system_clear_platform_error.cc',
       '<(DEPTH)/starboard/shared/win32/system_get_device_type.cc',
       '<(DEPTH)/starboard/shared/win32/system_get_property.cc',
+      '<(DEPTH)/starboard/shared/win32/system_get_total_cpu_memory.cc',
+      '<(DEPTH)/starboard/shared/win32/system_get_used_cpu_memory.cc',
       '<(DEPTH)/starboard/shared/win32/system_raise_platform_error.cc',
       '<(DEPTH)/starboard/shared/win32/window_create.cc',
       '<(DEPTH)/starboard/shared/win32/window_destroy.cc',
@@ -226,12 +238,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/drm_close_session.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
-        '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
         '<(DEPTH)/starboard/shared/stub/image_decode.cc',
         '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
         '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
@@ -334,8 +340,6 @@
         '<(DEPTH)/starboard/shared/win32/system_get_connection_type.cc',
         '<(DEPTH)/starboard/shared/win32/system_get_error_string.cc',
         '<(DEPTH)/starboard/shared/win32/system_get_number_of_processors.cc',
-        '<(DEPTH)/starboard/shared/win32/system_get_total_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/win32/system_get_used_cpu_memory.cc',
         '<(DEPTH)/starboard/shared/win32/socket_set_broadcast.cc',
         '<(DEPTH)/starboard/shared/win32/socket_set_receive_buffer_size.cc',
         '<(DEPTH)/starboard/shared/win32/socket_set_reuse_address.cc',
diff --git a/src/starboard/win/lib/atomic_public.h b/src/starboard/win/win32/lib/atomic_public.h
similarity index 81%
rename from src/starboard/win/lib/atomic_public.h
rename to src/starboard/win/win32/lib/atomic_public.h
index be4e805..b85fb12 100644
--- a/src/starboard/win/lib/atomic_public.h
+++ b/src/starboard/win/win32/lib/atomic_public.h
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
+#define STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
 
 #include "starboard/shared/win32/atomic_public.h"
 
-#endif  // STARBOARD_WIN_LIB_ATOMIC_PUBLIC_H_
+#endif  // STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/win/lib/configuration_public.h b/src/starboard/win/win32/lib/configuration_public.h
similarity index 82%
rename from src/starboard/win/lib/configuration_public.h
rename to src/starboard/win/win32/lib/configuration_public.h
index 800aeb1..8b5f8b1 100644
--- a/src/starboard/win/lib/configuration_public.h
+++ b/src/starboard/win/win32/lib/configuration_public.h
@@ -15,9 +15,9 @@
 // Other source files should never include this header directly, but should
 // include the generic "starboard/configuration.h" instead.
 
-#ifndef STARBOARD_WIN_LIB_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_WIN_LIB_CONFIGURATION_PUBLIC_H_
+#ifndef STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
 
 #include "starboard/win/shared/configuration_public.h"
 
-#endif  // STARBOARD_WIN_LIB_CONFIGURATION_PUBLIC_H_
+#endif  // STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/win/win32/lib/gyp_configuration.gypi b/src/starboard/win/win32/lib/gyp_configuration.gypi
new file mode 100644
index 0000000..7d8ddb2
--- /dev/null
+++ b/src/starboard/win/win32/lib/gyp_configuration.gypi
@@ -0,0 +1,51 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 0,
+    # TODO: In theory, there are tools that can combine static libraries into
+    # thick static libraries with all their transitive dependencies. Using
+    # shared_library here can have unexpected consequences. Explore building
+    # this into a thick static library instead.
+    'final_executable_type': 'shared_library',
+    'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
+    'sb_enable_lib': 1,
+    'angle_build_winrt': 0,
+    'winrt': 0,
+    'enable_d3d11_feature_level_11': 1,
+  },
+  'includes': [
+    '../../shared/gyp_configuration.gypi',
+  ],
+  'target_defaults': {
+    'default_configuration': 'win-win32-lib_debug',
+    'configurations': {
+
+      'win-win32-lib_debug': {
+        'inherit_from': ['win32_base', 'msvs_debug'],
+      },
+      'win-win32-lib_devel': {
+       'inherit_from': ['win32_base', 'msvs_devel'],
+      },
+      'win-win32-lib_qa': {
+        'inherit_from': ['win32_base', 'msvs_qa'],
+      },
+      'win-win32-lib_gold': {
+        'inherit_from': ['win32_base', 'msvs_gold'],
+      },
+    },  # end of configurations
+  },
+}
diff --git a/src/starboard/win/lib/gyp_configuration.py b/src/starboard/win/win32/lib/gyp_configuration.py
similarity index 88%
rename from src/starboard/win/lib/gyp_configuration.py
rename to src/starboard/win/win32/lib/gyp_configuration.py
index e136082..0d1deeb 100644
--- a/src/starboard/win/lib/gyp_configuration.py
+++ b/src/starboard/win/win32/lib/gyp_configuration.py
@@ -21,13 +21,13 @@
     os.path.realpath(
         os.path.join(
             os.path.dirname(__file__), os.pardir,
-            os.pardir, 'shared', 'win32')))
+            os.pardir, os.pardir, 'shared', 'win32')))
 import gyp_configuration
 
 
 def CreatePlatformConfig():
   try:
-    return gyp_configuration.PlatformConfig('win-lib')
+    return gyp_configuration.PlatformConfig('win-win32-lib')
   except RuntimeError as e:
     logging.critical(e)
     return None
diff --git a/src/starboard/win/win32/lib/starboard_platform.gyp b/src/starboard/win/win32/lib/starboard_platform.gyp
new file mode 100644
index 0000000..7216017
--- /dev/null
+++ b/src/starboard/win/win32/lib/starboard_platform.gyp
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../starboard_platform.gypi',
+  ],
+  'variables': {
+    'starboard_platform_dependent_files': [
+      '<@(base_win32_starboard_platform_dependent_files)',
+    ]
+  },
+}
diff --git a/src/starboard/win/lib/starboard_platform_tests.gyp b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
similarity index 96%
rename from src/starboard/win/lib/starboard_platform_tests.gyp
rename to src/starboard/win/win32/lib/starboard_platform_tests.gyp
index 1eb83ba..8961deb 100644
--- a/src/starboard/win/lib/starboard_platform_tests.gyp
+++ b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
@@ -38,7 +38,7 @@
       'variables': {
         'executable_name': 'starboard_platform_tests',
       },
-      'includes': [ '../../build/deploy.gypi' ],
+      'includes': [ '../../../build/deploy.gypi' ],
     },
   ],
 }
diff --git a/src/starboard/win/lib/thread_types_public.h b/src/starboard/win/win32/lib/thread_types_public.h
similarity index 81%
rename from src/starboard/win/lib/thread_types_public.h
rename to src/starboard/win/win32/lib/thread_types_public.h
index 0f8f791..adc6221 100644
--- a/src/starboard/win/lib/thread_types_public.h
+++ b/src/starboard/win/win32/lib/thread_types_public.h
@@ -14,9 +14,9 @@
 
 // Includes threading primitive types and initializers.
 
-#ifndef STARBOARD_WIN_LIB_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_WIN_LIB_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
 
 #include "starboard/shared/win32/thread_types_public.h"
 
-#endif  // STARBOARD_WIN_LIB_THREAD_TYPES_PUBLIC_H_
+#endif  // STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/win/win32/starboard_platform.gyp b/src/starboard/win/win32/starboard_platform.gyp
index 9373b0a..2027d45 100644
--- a/src/starboard/win/win32/starboard_platform.gyp
+++ b/src/starboard/win/win32/starboard_platform.gyp
@@ -1,40 +1,24 @@
-# Copyright 2017 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-{
-  'includes': [
-        '../shared/starboard_platform.gypi',
-    ],
-    'variables': {
-      'starboard_platform_dependent_files': [
-        'atomic_public.h',
-        'configuration_public.h',
-        'thread_types_public.h',
-        '../shared/system_get_path.cc',
-        'main.cc',
-        '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-        '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-        '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-        '<@(uwp_incompatible_win32)',
-        '<@(stub_media_player)'
-      ]
-    },
-  }
+# Copyright 2017 Google Inc. All Rights Reserved.

+#

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

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

+# You may obtain a copy of the License at

+#

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

+#

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

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

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

+# See the License for the specific language governing permissions and

+# limitations under the License.

+{

+  'includes': [

+    'starboard_platform.gypi',

+  ],

+  'variables': {

+    'starboard_platform_dependent_files': [

+      'main.cc',

+      '<@(base_win32_starboard_platform_dependent_files)',

+    ]

+  },

+}

diff --git a/src/starboard/win/win32/starboard_platform.gypi b/src/starboard/win/win32/starboard_platform.gypi
new file mode 100644
index 0000000..254ae7b
--- /dev/null
+++ b/src/starboard/win/win32/starboard_platform.gypi
@@ -0,0 +1,42 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../shared/starboard_platform.gypi',
+  ],
+  'variables': {
+    'base_win32_starboard_platform_dependent_files': [
+      'atomic_public.h',
+      'configuration_public.h',
+      'thread_types_public.h',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
+      '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
+      '<(DEPTH)/starboard/win/shared/system_get_path.cc',
+      '<@(uwp_incompatible_win32)',
+      '<@(stub_media_player)',
+      '<@(stub_drm_system)' ,
+    ],
+  },
+}