Merge "Add API to cancel outgoing calls"
diff --git a/Android.mk b/Android.mk
index 43b7e59..d504481 100644
--- a/Android.mk
+++ b/Android.mk
@@ -306,6 +306,7 @@
 	media/java/android/media/IRemoteControlDisplay.aidl \
 	media/java/android/media/IRemoteDisplayCallback.aidl \
 	media/java/android/media/IRemoteDisplayProvider.aidl \
+	media/java/android/media/IRemoteVolumeController.aidl \
 	media/java/android/media/IRemoteVolumeObserver.aidl \
 	media/java/android/media/IRingtonePlayer.aidl \
 	media/java/android/media/IVolumeController.aidl \
@@ -330,6 +331,7 @@
 	telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl \
+	telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
 	telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
@@ -823,7 +825,6 @@
 		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
 		-toroot / \
 		-hdf android.whichdoc online \
-		-briefdocs \
 		$(sample_groups) \
 		-hdf android.hasSamples true \
 		-samplesdir $(samples_dir)
diff --git a/api/current.txt b/api/current.txt
index 4b7fa23..fbbae6c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15751,6 +15751,7 @@
   public final class MediaController {
     method public void addCallback(android.media.session.MediaController.Callback);
     method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
+    method public void adjustVolumeBy(int, int);
     method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public static android.media.session.MediaController fromToken(android.media.session.MediaSessionToken);
     method public android.media.MediaMetadata getMetadata();
@@ -15760,6 +15761,7 @@
     method public android.media.session.MediaController.VolumeInfo getVolumeInfo();
     method public void removeCallback(android.media.session.MediaController.Callback);
     method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+    method public void setVolumeTo(int, int);
   }
 
   public static abstract class MediaController.Callback {
@@ -15767,6 +15769,7 @@
     method public void onMetadataChanged(android.media.MediaMetadata);
     method public void onPlaybackStateChanged(android.media.session.PlaybackState);
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public void onVolumeInfoChanged(android.media.session.MediaController.VolumeInfo);
   }
 
   public final class MediaController.TransportControls {
@@ -15809,8 +15812,8 @@
     method public void setPlaybackToRemote(android.media.VolumeProvider);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
-    field public static final int VOLUME_TYPE_LOCAL = 1; // 0x1
-    field public static final int VOLUME_TYPE_REMOTE = 2; // 0x2
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 788ac56..88746bf4 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -652,6 +652,12 @@
         public int userId;
 
         /**
+         * The last time this task was active.
+         * @hide
+         */
+        public long lastActiveTime;
+
+        /**
          * The recent activity values for the highest activity in the stack to have set the values.
          * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
          *
@@ -688,6 +694,7 @@
             }
             dest.writeInt(stackId);
             dest.writeInt(userId);
+            dest.writeLong(lastActiveTime);
         }
 
         public void readFromParcel(Parcel source) {
@@ -700,6 +707,7 @@
                     TaskDescription.CREATOR.createFromParcel(source) : null;
             stackId = source.readInt();
             userId = source.readInt();
+            lastActiveTime = source.readLong();
         }
 
         public static final Creator<RecentTaskInfo> CREATOR
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index ad4ccbb..6fbf87d 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -26,6 +26,7 @@
 interface ITrustManager {
     void reportUnlockAttempt(boolean successful, int userId);
     void reportEnabledTrustAgentsChanged(int userId);
+    void reportRequireCredentialEntry(int userId);
     void registerTrustListener(in ITrustListener trustListener);
     void unregisterTrustListener(in ITrustListener trustListener);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index e31c624..6e90590 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -71,6 +71,21 @@
     }
 
     /**
+     * Reports that trust is disabled until credentials have been entered for user {@param userId}.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     *
+     * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
+     */
+    public void reportRequireCredentialEntry(int userId) {
+        try {
+            mService.reportRequireCredentialEntry(userId);
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
      * Registers a listener for trust events.
      *
      * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index e2f88eb..ef8c67b 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1354,7 +1354,11 @@
     /**
      * <p>Range of valid exposure
      * times used by {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}.</p>
+     * <p>The min value will be &lt;= 100e3 (100 us). For FULL
+     * capability devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} == FULL),
+     * max will be &gt;= 100e6 (100ms)</p>
      *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
     public static final Key<android.util.Range<Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE =
@@ -1371,7 +1375,10 @@
      * <p>Refer to
      * StreamConfigurationMap#getOutputMinFrameDuration(int,Size)
      * for the minimum frame duration values.</p>
+     * <p>For FULL capability devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} == FULL),
+     * max will be &gt;= 100e6 (100ms).</p>
      *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      */
     public static final Key<Long> SENSOR_INFO_MAX_FRAME_DURATION =
@@ -1748,18 +1755,26 @@
     /**
      * <p>Generally classifies the overall set of the camera device functionality.</p>
      * <p>Camera devices will come in two flavors: LIMITED and FULL.</p>
-     * <p>A FULL device has the most support possible and will enable the
-     * widest range of use cases such as:</p>
+     * <p>A FULL device has the most support possible and will support below capabilities:</p>
      * <ul>
      * <li>30fps at maximum resolution (== sensor resolution) is preferred, more than 20fps is required.</li>
      * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
      * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li>
      * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_POST_PROCESSING)</li>
+     * <li>Arbitrary cropping region ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>==</code> FREEFORM)</li>
+     * <li>At least 3 processed (but not stalling) format output streams ({@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC android.request.maxNumOutputProc} <code>&gt;=</code> 3)</li>
+     * <li>The required stream configuration defined in android.scaler.availableStreamConfigurations</li>
+     * <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
+     * <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
      * </ul>
      * <p>A LIMITED device may have some or none of the above characteristics.
      * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC
+     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+     * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
+     * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
      * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index dad1854..889b127 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -310,7 +310,9 @@
      * <li>{@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}</li>
      * </ul>
      * </li>
-     * <li>Lens shading map information<ul>
+     * <li>Manual lens shading map control<ul>
+     * <li>{@link CaptureRequest#SHADING_MODE android.shading.mode}</li>
+     * <li>{@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode}</li>
      * <li>android.statistics.lensShadingMap</li>
      * <li>android.lens.info.shadingMapSize</li>
      * </ul>
@@ -323,6 +325,8 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
+     * @see CaptureRequest#SHADING_MODE
+     * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @see CaptureRequest#TONEMAP_CURVE
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index f702556..1646120 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -19,6 +19,7 @@
 import android.graphics.ImageFormat;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Parameters;
 import android.hardware.Camera.Size;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -47,6 +48,8 @@
     private static final String TAG = "LegacyMetadataMapper";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
+    private static final long NS_PER_MS = 1000000;
+
     // from graphics.h
     private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
     private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
@@ -54,10 +57,21 @@
     // for metadata
     private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
 
-    private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
-    private static final long APPROXIMATE_SENSOR_AREA = (1 << 23); // 8mp
-    private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
-    private static final long NS_PER_MS = 1000000;
+    private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_RAW = 0; // no raw support
+    private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC = 3; // preview, video, cb
+    private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC_STALL = 1; // 1 jpeg only
+    private static final int REQUEST_MAX_NUM_INPUT_STREAMS_COUNT = 0; // no reprocessing
+
+    /** Assume 3 HAL1 stages: Exposure, Read-out, Post-Processing */
+    private static final int REQUEST_PIPELINE_MAX_DEPTH_HAL1 = 3;
+    /** Assume 3 shim stages: Preview input, Split output, Format conversion for output */
+    private static final int REQUEST_PIPELINE_MAX_DEPTH_OURS = 3;
+    /* TODO: Update above maxDepth values once we do more performance measurements */
+
+    // For approximating JPEG stall durations
+    private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // 200 milliseconds
+    private static final long APPROXIMATE_SENSOR_AREA_PX = (1 << 23); // 8 megapixels
+    private static final long APPROXIMATE_JPEG_ENCODE_TIME_MS = 600; // 600 milliseconds
 
     /*
      * Development hijinks: Lie about not supporting certain capabilities
@@ -113,11 +127,11 @@
 
         CameraMetadataNative m = new CameraMetadataNative();
 
-        mapCameraInfo(m, info.info);
+        mapCharacteristicsFromInfo(m, info.info);
 
         Camera.Parameters params = Camera.getEmptyParameters();
         params.unflatten(parameters);
-        mapCameraParameters(m, params);
+        mapCharacteristicsFromParameters(m, params);
 
         if (VERBOSE) {
             Log.v(TAG, "createCharacteristics metadata:");
@@ -129,21 +143,51 @@
         return new CameraCharacteristics(m);
     }
 
-    private static void mapCameraInfo(CameraMetadataNative m, CameraInfo i) {
+    private static void mapCharacteristicsFromInfo(CameraMetadataNative m, CameraInfo i) {
         m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
                 LENS_FACING_BACK : LENS_FACING_FRONT);
         m.set(SENSOR_ORIENTATION, i.orientation);
     }
 
-    private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
+    private static void mapCharacteristicsFromParameters(CameraMetadataNative m,
+            Camera.Parameters p) {
+        /*
+         * info.supportedHardwareLevel
+         */
         m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
         mapStreamConfigs(m, p);
+        /*
+         * control.ae*
+         */
         mapControlAe(m, p);
+        /*
+         * control.awb*
+         */
         mapControlAwb(m, p);
-        mapCapabilities(m, p);
+        /*
+         * control.*
+         * - Anything that doesn't have a set of related fields
+         */
+        mapControlOther(m, p);
+        /*
+         * lens.*
+         */
         mapLens(m, p);
+        /*
+         * flash.*
+         */
         mapFlash(m, p);
+
+        /*
+         * request.*
+         */
+        mapRequest(m, p);
         // TODO: map other fields
+
+        /*
+         * sync.*
+         */
+        mapSync(m, p);
     }
 
     private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
@@ -179,9 +223,15 @@
         List<Camera.Size> jpegSizes = p.getSupportedPictureSizes();
         appendStreamConfig(availableStreamConfigs,
                 HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
+        /*
+         * scaler.availableStreamConfigurations
+         */
         m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
                 availableStreamConfigs.toArray(new StreamConfiguration[0]));
 
+        /*
+         * scaler.availableMinFrameDurations
+         */
         // No frame durations available
         m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[0]);
 
@@ -197,9 +247,15 @@
                 longestStallDuration = stallDuration;
             }
         }
+        /*
+         * scaler.availableStallDurations
+         */
         // Set stall durations for jpeg, other formats use default stall duration
         m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
 
+        /*
+         * sensor.info.maxFrameDuration
+         */
         m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
     }
 
@@ -289,9 +345,8 @@
         }
     }
 
-    private static void mapCapabilities(CameraMetadataNative m, Camera.Parameters p) {
-        int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
-        m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+    private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) {
+        // TODO
     }
 
     private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
@@ -300,6 +355,9 @@
          *  but if it's not, we can't tell the minimum focus distance, so leave it null then.
          */
         if (p.getFocusMode() == Camera.Parameters.FOCUS_MODE_FIXED) {
+            /*
+             * lens.info.minimumFocusDistance
+             */
             m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
         }
     }
@@ -321,9 +379,51 @@
             flashAvailable = false;
         }
 
+        /*
+         * flash.info.available
+         */
         m.set(FLASH_INFO_AVAILABLE, flashAvailable);
     }
 
+    private static void mapRequest(CameraMetadataNative m, Parameters p) {
+        /*
+         * request.availableCapabilities
+         */
+        int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
+        m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+
+        /*
+         * request.maxNumOutputStreams
+         */
+        int[] outputStreams = {
+                /* RAW */
+                REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_RAW,
+                /* Processed & Not-Stalling */
+                REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC,
+                /* Processed & Stalling */
+                REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC_STALL,
+        };
+        m.set(REQUEST_MAX_NUM_OUTPUT_STREAMS, outputStreams);
+
+        /*
+         * request.maxNumInputStreams
+         */
+        m.set(REQUEST_MAX_NUM_INPUT_STREAMS, REQUEST_MAX_NUM_INPUT_STREAMS_COUNT);
+
+        /*
+         * request.pipelineMaxDepth
+         */
+        m.set(REQUEST_PIPELINE_MAX_DEPTH,
+                (byte)(REQUEST_PIPELINE_MAX_DEPTH_HAL1 + REQUEST_PIPELINE_MAX_DEPTH_OURS));
+    }
+
+    private static void mapSync(CameraMetadataNative m, Parameters p) {
+        /*
+         * sync.maxLatency
+         */
+        m.set(SYNC_MAX_LATENCY, SYNC_MAX_LATENCY_UNKNOWN);
+    }
+
     private static void appendStreamConfig(
             ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
         for (Camera.Size size : sizes) {
@@ -398,8 +498,8 @@
     private static long calculateJpegStallDuration(Camera.Size size) {
         long baseDuration = APPROXIMATE_CAPTURE_DELAY_MS * NS_PER_MS; // 200ms for capture
         long area = size.width * (long) size.height;
-        long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME * NS_PER_MS /
-                APPROXIMATE_SENSOR_AREA; // 600ms stall for 8mp
+        long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME_MS * NS_PER_MS /
+                APPROXIMATE_SENSOR_AREA_PX; // 600ms stall for 8mp
         return baseDuration + area * stallPerArea;
     }
 
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index b02a79d..9da3a51 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -161,6 +161,7 @@
     private boolean mFinished;
     private boolean mCanDoze;
     private boolean mDozing;
+    private boolean mWindowless;
     private DozeHardware mDozeHardware;
 
     private boolean mDebug = false;
@@ -520,6 +521,24 @@
     }
 
     /**
+     * Marks this dream as windowless.  Only available to doze dreams.
+     *
+     * @hide
+     */
+    public void setWindowless(boolean windowless) {
+        mWindowless = windowless;
+    }
+
+    /**
+     * Returns whether or not this dream is windowless.  Only available to doze dreams.
+     *
+     * @hide
+     */
+    public boolean isWindowless() {
+        return mWindowless;
+    }
+
+    /**
      * Returns true if this dream is allowed to doze.
      * <p>
      * The value returned by this method is only meaningful when the dream has started.
@@ -715,6 +734,7 @@
             WindowManagerGlobal.getInstance().closeAll(mWindowToken,
                     this.getClass().getName(), "Dream");
             mWindowToken = null;
+            mCanDoze = false;
         }
     }
 
@@ -744,47 +764,50 @@
 
         mWindowToken = windowToken;
         mCanDoze = canDoze;
-
-        mWindow = PolicyManager.makeNewWindow(this);
-        mWindow.setCallback(this);
-        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-        mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
-        mWindow.setFormat(PixelFormat.OPAQUE);
-
-        if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
-                windowToken, WindowManager.LayoutParams.TYPE_DREAM));
-
-        WindowManager.LayoutParams lp = mWindow.getAttributes();
-        lp.type = WindowManager.LayoutParams.TYPE_DREAM;
-        lp.token = windowToken;
-        lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
-        lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                    | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
-                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
-                    | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
-                    | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
-                    );
-        mWindow.setAttributes(lp);
-        mWindow.setWindowManager(null, windowToken, "dream", true);
-
-        applySystemUiVisibilityFlags(
-                (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
-                View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
-        try {
-            getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
-        } catch (WindowManager.BadTokenException ex) {
-            // This can happen because the dream manager service will remove the token
-            // immediately without necessarily waiting for the dream to start.
-            // We should receive a finish message soon.
-            Slog.i(TAG, "attach() called after window token already removed, dream will "
-                    + "finish soon");
-            mWindow = null;
-            return;
+        if (mWindowless && !mCanDoze) {
+            throw new IllegalStateException("Only doze dreams can be windowless");
         }
+        if (!mWindowless) {
+            mWindow = PolicyManager.makeNewWindow(this);
+            mWindow.setCallback(this);
+            mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+            mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
+            mWindow.setFormat(PixelFormat.OPAQUE);
 
+            if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
+                    windowToken, WindowManager.LayoutParams.TYPE_DREAM));
+
+            WindowManager.LayoutParams lp = mWindow.getAttributes();
+            lp.type = WindowManager.LayoutParams.TYPE_DREAM;
+            lp.token = windowToken;
+            lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
+            lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                        | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+                        | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
+                        | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
+                        );
+            mWindow.setAttributes(lp);
+            mWindow.setWindowManager(null, windowToken, "dream", true);
+
+            applySystemUiVisibilityFlags(
+                    (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
+                    View.SYSTEM_UI_FLAG_LOW_PROFILE);
+
+            try {
+                getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
+            } catch (WindowManager.BadTokenException ex) {
+                // This can happen because the dream manager service will remove the token
+                // immediately without necessarily waiting for the dream to start.
+                // We should receive a finish message soon.
+                Slog.i(TAG, "attach() called after window token already removed, dream will "
+                        + "finish soon");
+                mWindow = null;
+                return;
+            }
+        }
         // We need to defer calling onDreamingStarted until after onWindowAttached,
         // which is posted to the handler by addView, so we post onDreamingStarted
         // to the handler also.  Need to watch out here in case detach occurs before
@@ -792,7 +815,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mWindow != null) {
+                if (mWindow != null || mWindowless) {
                     if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
                     mStarted = true;
                     onDreamingStarted();
@@ -878,6 +901,7 @@
                 if (isLowProfile()) pw.print(" lowprofile");
                 if (isFullscreen()) pw.print(" fullscreen");
                 if (isScreenBright()) pw.print(" bright");
+                if (isWindowless()) pw.print(" windowless");
                 if (isDozing()) pw.print(" dozing");
                 pw.println();
             }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d31c5cc..5bd6f52 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1460,4 +1460,11 @@
         }
         return activeTrustAgents;
     }
+
+    /**
+     * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int)
+     */
+    public void requireCredentialEntry(int userId) {
+        getTrustManager().reportRequireCredentialEntry(userId);
+    }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 53f46eb..ca897d5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -156,16 +156,12 @@
         android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
     <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
-    <protected-broadcast android:name="android.btopp.intent.action.BT_OPP_HANDOVER_STARTED" />
-    <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" />
     <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
-    <protected-broadcast android:name="android.btopp.intent.action.BT_OPP_TRANSFER_PROGRESS" />
     <protected-broadcast android:name="android.btopp.intent.action.LIST" />
     <protected-broadcast android:name="android.btopp.intent.action.OPEN_OUTBOUND" />
     <protected-broadcast android:name="android.btopp.intent.action.HIDE_COMPLETE" />
     <protected-broadcast android:name="android.btopp.intent.action.CONFIRM" />
     <protected-broadcast android:name="android.btopp.intent.action.HIDE" />
-    <protected-broadcast android:name="android.btopp.intent.action.BT_OPP_TRANSFER_DONE" />
     <protected-broadcast android:name="android.btopp.intent.action.RETRY" />
     <protected-broadcast android:name="android.btopp.intent.action.OPEN" />
     <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />
@@ -284,7 +280,13 @@
     <protected-broadcast
         android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
     <!-- Defined in RestrictionsManager -->
+
     <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_STARTED" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_PROGRESS" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
+
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
@@ -877,6 +879,14 @@
 	android:permissionGroup="android.permission-group.NETWORK"
 	android:protectionLevel="signature|system" />
 
+    <!-- Allows sending and receiving handover transfer status from Wifi and Bluetooth
+         @hide
+    -->
+    <permission android:name="android.permission.NFC_HANDOVER_STATUS"
+                android:label="@string/permlab_handoverStatus"
+                android:description="@string/permdesc_handoverStatus"
+                android:protectionLevel="signature|system" />
+
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
     <!-- ================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d350ef2..708fe67 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -116,8 +116,10 @@
          specified for -large and -xlarge configurations. -->
     <dimen name="config_prefDialogWidth">320dp</dimen>
 
-    <!-- Enables or disables fading edges when marquee is enabled in TextView. -->
-    <bool name="config_ui_enableFadingMarquee">true</bool>
+    <!-- Enables or disables fading edges when marquee is enabled in TextView.
+         Off by default, since the framebuffer readback used to implement the
+         fading edges is prohibitively expensive on most GPUs. -->
+    <bool name="config_ui_enableFadingMarquee">false</bool>
 
     <!-- Whether dialogs should close automatically when the user touches outside
          of them.  This should not normally be modified. -->
@@ -1184,7 +1186,7 @@
     <bool name="config_dreamsActivatedOnDockByDefault">true</bool>
     <!-- If supported and enabled, are dreams activated when asleep and charging? (by default) -->
     <bool name="config_dreamsActivatedOnSleepByDefault">false</bool>
-    <!-- ComponentName of the default dream (Settings.Secure.SCREENSAVER_COMPONENT) -->
+    <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
     <string name="config_dreamsDefaultComponent">com.google.android.deskclock/com.android.deskclock.Screensaver</string>
 
     <!-- Are we allowed to dream while not plugged in? -->
@@ -1205,8 +1207,7 @@
          try to start this dream if possible.  The dream should typically call startDozing()
          to put the display into a low power state and allow the application processor
          to be suspended.  When the dream ends, the system will go to sleep as usual.
-         Specify the component name (Settings.Secure.SCREENSAVER_COMPONENT) or an
-         empty string if none.
+         Specify the component name or an empty string if none.
 
          Note that doze dreams are not subject to the same start conditions as ordinary dreams.
          Doze dreams will run whenever the power manager is in a dozing state. -->
@@ -1570,6 +1571,7 @@
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
+        <item>lockdown</item>
         <item>bugreport</item>
         <item>users</item>
     </string-array>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3da6669..6e92b07 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -443,6 +443,9 @@
     <!-- label for item that launches settings in phone options dialog [CHAR LIMIT=15]-->
     <string name="global_action_settings">Settings</string>
 
+    <!-- label for item that locks the phone and enforces that it can't be unlocked without entering a credential. [CHAR LIMIT=15] -->
+    <string name="global_action_lockdown">Lock now</string>
+
     <!-- Text to use when the number in a notification info is too large
          (greater than status_bar_notification_info_maxnum, defined in
          values/config.xml) and must be truncated. May need to be localized
@@ -2137,6 +2140,9 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessDrmCertificates">Allows an application to provision and use DRM certficates. Should never be needed for normal apps.</string>
 
+    <string name="permlab_handoverStatus">Receive handover transfer broadcasts.</string>
+    <string name="permdesc_handoverStatus">Allows receiving handover transfer status information.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3e82d08..7442459 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1425,6 +1425,7 @@
   <java-symbol type="string" name="global_action_silent_mode_off_status" />
   <java-symbol type="string" name="global_action_silent_mode_on_status" />
   <java-symbol type="string" name="global_action_toggle_silent_mode" />
+  <java-symbol type="string" name="global_action_lockdown" />
   <java-symbol type="string" name="invalidPuk" />
   <java-symbol type="string" name="lockscreen_carrier_default" />
   <java-symbol type="style" name="Animation.LockScreen" />
diff --git a/docs/html/auto/images/logos/auto/renault.png b/docs/html/auto/images/logos/auto/renault.png
index 65a4ae3..d252aa3 100644
--- a/docs/html/auto/images/logos/auto/renault.png
+++ b/docs/html/auto/images/logos/auto/renault.png
Binary files differ
diff --git a/docs/html/auto/index.jd b/docs/html/auto/index.jd
index 116f26d..63ac287 100644
--- a/docs/html/auto/index.jd
+++ b/docs/html/auto/index.jd
@@ -59,6 +59,9 @@
   padding-left:5px;
   padding-right:15px;
 }
+.cols-leftp {
+  padding-left:95px;
+}
 </style>
 
 
@@ -325,7 +328,7 @@
                 Android Auto is coming soon to new cars from these manufacturers
               </div>
           </div>
-          <div class="cols">
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/abarth.png"
                    width="120" height="120" class="img-logo" />
@@ -342,12 +345,12 @@
               <img src="{@docRoot}auto/images/logos/auto/audi.png"
                    width="120" height="120" class="img-logo" />
             </div>
+          </div>
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/bentley.png"
                    width="120" height="120" class="img-logo" />
             </div>
-          </div>
-          <div class="cols">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/chevrolet.png"
                    width="120" height="120" class="img-logo" />
@@ -360,6 +363,8 @@
               <img src="{@docRoot}auto/images/logos/auto/dodge.png"
                    width="120" height="120" class="img-logo" />
             </div>
+          </div>
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/fiat.png"
                    width="120" height="120" class="img-logo" />
@@ -368,8 +373,6 @@
               <img src="{@docRoot}auto/images/logos/auto/ford.png"
                    width="120" height="120" class="img-logo" />
             </div>
-          </div>
-          <div class="cols">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/honda.png"
                    width="120" height="120" class="img-logo" />
@@ -378,6 +381,8 @@
               <img src="{@docRoot}auto/images/logos/auto/hyundai.png"
                    width="120" height="120" class="img-logo" />
             </div>
+          </div>
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/infinity.png"
                    width="120" height="120" class="img-logo" />
@@ -390,12 +395,12 @@
               <img src="{@docRoot}auto/images/logos/auto/kia.png"
                    width="120" height="120" class="img-logo" />
             </div>
-          </div>
-          <div class="cols">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/maserati.png"
                    width="120" height="120" class="img-logo" />
             </div>
+          </div>
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/mazda.png"
                    width="120" height="120" class="img-logo" />
@@ -413,7 +418,7 @@
                    width="120" height="120" class="img-logo" />
             </div>
           </div>
-          <div class="cols">
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/ram.png"
                    width="120" height="120" class="img-logo" />
@@ -430,12 +435,12 @@
               <img src="{@docRoot}auto/images/logos/auto/skoda.png"
                    width="120" height="120" class="img-logo" />
             </div>
+          </div>
+          <div class="cols cols-leftp">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/subaru.png"
                    width="120" height="120" class="img-logo" />
             </div>
-          </div>
-          <div class="cols">
             <div class="col-5">
               <img src="{@docRoot}auto/images/logos/auto/suzuki.png"
                    width="120" height="120" class="img-logo" />
@@ -448,14 +453,6 @@
               <img src="{@docRoot}auto/images/logos/auto/volvo.png"
                    width="120" height="120" class="img-logo" />
             </div>
-            <div class="col-5">
-              <!--<img src="/auto/images/logos/auto/skoda.png"
-                   width="120" height="120" class="img-logo" />-->
-            </div>
-            <div class="col-5">
-              <!--<img src="/auto/images/logos/auto/skoda.png"
-                   width="120" height="120" class="img-logo" />-->
-            </div>
           </div>
         </div>
       </div>
diff --git a/docs/html/images/home/auto-wordmark.png b/docs/html/images/home/auto-wordmark.png
new file mode 100644
index 0000000..027dfca
--- /dev/null
+++ b/docs/html/images/home/auto-wordmark.png
Binary files differ
diff --git a/docs/html/images/home/auto.png b/docs/html/images/home/auto.png
index 7ea01b0..4ce882a 100644
--- a/docs/html/images/home/auto.png
+++ b/docs/html/images/home/auto.png
Binary files differ
diff --git a/docs/html/images/home/tv-wordmark.png b/docs/html/images/home/tv-wordmark.png
new file mode 100644
index 0000000..e7660ac
--- /dev/null
+++ b/docs/html/images/home/tv-wordmark.png
Binary files differ
diff --git a/docs/html/images/home/tv.png b/docs/html/images/home/tv.png
index 3cf2034..7208a99 100644
--- a/docs/html/images/home/tv.png
+++ b/docs/html/images/home/tv.png
Binary files differ
diff --git a/docs/html/images/home/wear-wordmark.png b/docs/html/images/home/wear-wordmark.png
new file mode 100644
index 0000000..e645887
--- /dev/null
+++ b/docs/html/images/home/wear-wordmark.png
Binary files differ
diff --git a/docs/html/images/home/wear.png b/docs/html/images/home/wear.png
index dfaded7..e6602fd 100644
--- a/docs/html/images/home/wear.png
+++ b/docs/html/images/home/wear.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 2f01538..f101ad2 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -65,20 +65,22 @@
           and it now supports these exciting, new form-factors.
         </div>
       </div>
-      <div class="landing-body" style="margin-top: 80px;">
+      <div class="landing-body" style="margin-top: 50px;">
         <div class="landing-breakout cols">
           <div class="col-3-wide">
+              <img src="{@docRoot}images/home/wear-wordmark.png" />
               <img src="{@docRoot}images/home/wear.png">
-              <p class="landing-small" style="margin-top:30px">
+              <p class="landing-small">
                 Provide information on-the-go for your users, whenever they need it.
             </p>
             <p class="landing-small">
               <a href="{@docRoot}wear/index.html">Learn about Android Wear</a>
             </p>
           </div>
-          <div class="col-3-wide">            
+          <div class="col-3-wide">
+              <img src="{@docRoot}images/home/tv-wordmark.png" />
              <img src="{@docRoot}images/home/tv.png">
-              <p class="landing-small" style="margin-top:30px">
+              <p class="landing-small" >
                 Build your apps for the big screen and bring your content to life.
               </p>
             <p class="landing-small">
@@ -86,9 +88,10 @@
 
             </p>
           </div>
-          <div class="col-3-wide">            
+          <div class="col-3-wide">
+              <img src="{@docRoot}images/home/auto-wordmark.png" />
               <img src="{@docRoot}images/home/auto.png">
-              <p class="landing-small" style="margin-top:30px">
+              <p class="landing-small">
                 Extend your music apps to automobile
                 entertainment systems.
              </p>
diff --git a/docs/html/preview/google-play-services-wear.html b/docs/html/preview/google-play-services-wear.html
index ad8891f..f225f7a 100644
--- a/docs/html/preview/google-play-services-wear.html
+++ b/docs/html/preview/google-play-services-wear.html
@@ -16,55 +16,55 @@
 <div class="col-12" id="doc-col">
 
 <h1 itemprop="name">Google Play services Preview for Wear</h1>
-      
-    
-  
 
+<div id="jd-content">
+<div class="jd-descr" itemprop="articleBody">
+<p>Google Play services 5.0  is currently being rolled out to the world.
+We usually wait to release the Google Play services SDK until the rollout completes.
+This ensures that your newly-updated apps can run on the most devices as possible.</p>
 
-  
-  <div id="jd-content">
-  <div class="jd-descr" itemprop="articleBody">
-<p>The Google Play services app is currently being rolled out to the hundreds of millions of
-Android devices and will complete in early July. Because of this, we usually wait to release the Google Play
-services SDK until all users receive the app. This guarantees that your newly-updated apps can
-run on the most devices as possible.</p>
+<p>However, if you want to develop for Android Wear now, complete the following steps to get
+special access to all the things you need to start developing, without waiting for the rollout to
+complete.</p>
 
-<p>However, if want to develop for Android Wear now, complete the following steps
-to get special access to all the things you need to start developing, without waiting
-for the rollout to complete. </p>
-
-<p class="warning"><b>Warning</b>: Do not publish any apps that use the new Google Play services
-APIs until the rollout is complete. Your apps will break on most user devices, which will
-degrade your user rating.</b>
+<p class="warning"><b>Warning</b>: Do not publish any apps that are built with Google Play services
+SDK 5.0 until the rollout is completed over the coming days.  Your apps will break on most user
+devices, which will degrade your apps’ user experience. We’ll publish an update to the
+<a href="http://android-developers.blogspot.com/">Android Developer blog</a> when the rollout is done.
 </p>
 
 <h2 style="margin-bottom: 0px;">1. Get Whitelisted for the Preview</h2><hr>
 
-<p>If you attended Google I/O, your registered Gmail account is automatically whitelisted for these
-preview resources. If you didn't attend Google I/O, sign up below to get access:</p>
+<p>If you attended Google I/O, your registered Google account is automatically whitelisted for
+these preview resources. You're done! Head to Step 2.</p>
 
-<a href="https://groups.google.com/forum/?hl=en#!forum/io14androidweardev">Get Whitelisted</a>
+<p><stong><em>If you didn't attend Google I/O</em></strong> or want to use a different account,
+click following link to
+<a href="https://groups.google.com/group/io14androidweardev/subscribe" target="_blank">join
+the Google Group</a> and get whitelisted.</p>
 
 <h2 style="margin-bottom: 0px;">2. Download Required Apps</h2><hr>
 <p>You'll need the following apps to get the most out of Android Wear:</p>
 
-
-<p>Here's a list of the apps you need:</p>
 <ul>
-<li><a href="https://play.google.com/apps/testing/com.google.android.gms">Google Play services</a>: Allows your Wear device to properly communicate with your handheld device. This is
-required to use the Android Wear Companion App.</li>
+<li><a href="https://play.google.com/apps/testing/com.google.android.gms">Google Play services</a>:
+Allows your Android Wear device to communicate with your handheld device. This is required to
+use the Android Wear app and other apps listed below.</li>
   <li><a href="https://play.google.com/apps/testing/com.google.android.wearable.app">Android Wear
-  Companion</a>: The main user app to pair a handheld to a wearable and to provide syncing
-  of notifications and data.</li>
+  Companion</a>: The app for pairing a handheld to a wearable and providing syncing of
+  notifications and data
+  </li>
   <li><a href="https://play.google.com/apps/testing/com.google.android.googlequicksearchbox">Google
-  Search</a>: A preview release of the Google Search handheld app that Wear communicates with
-  to carry out searches.</li>
-  <li><a href="https://play.google.com/apps/testing/com.google.android.keep">Google Keep</a>: To enable the "Take a note" command</li>
-  <li><a href="https://play.google.com/apps/testing/com.google.samples.apps.iosched">Google I/O 2014</a></li>
+  Search</a>: Enables searches from Android Wear</li>
+  <li><a href="https://play.google.com/apps/testing/com.google.android.keep">Google Keep</a>:
+  Supports the "Take a note" command</li>
+  <li><a href="https://play.google.com/apps/testing/com.google.samples.apps.iosched">Google I/O
+  2014</a>:
+  Supports session feedback from Android Wear</li>
  </ul>
 
-<p>To enable the preview versions of the apps, click each app link above and follow these
-instructions:</p>
+<p>To obtain these apps from Google Play, click each app link above and follow these instructions,
+preferably from your mobile browser:</p>
 
 <ol>
  <li>Click the <b>Become a Tester</b> button to opt-in to the preview version of the app. The page
@@ -73,23 +73,31 @@
  Store download page to get the app. The
  following screenshot shows how the opt-in process looks like:
 <img style="margin-top:40px" src="/preview/images/opt-in.png"></li>
- <li>When Google Play services is rolled out to all devices, go back to the app links provided
-  to opt-out of the preview versions of the apps. Check back here in a week for the status of
-  the rollout.</li>
+</ol>
+<h2 style="margin-bottom: 0px;">3. Start Building</h2><hr>
+
+<p>The Google Play services SDK is required if you want to sync and send data between wearable
+and handheld devices. To get the new SDK that is compatible with the Google Play services
+APK that you just installed, follow these steps: </p>
+
+<p class="note"><b>Note:</b> We highly recommend you use Android Studio for Wear development.</p>
+<ol>
+  <li>Start AVD Manager.</li>
+  <li>Update the Android SDK Tools and Platform-tools to versions 23 and 20 respectively.</li>
+  <li>Click <b>Tools > Manage Add-on Sites > User Defined Sites</b>.</li>
+  <li>Click <b>New</b>, enter
+  <code>https://dl-ssl.google.com/android/repository/addon-play-services-5.xml</code> into the
+  text field, and click <b>OK</b>.</li>
+  <li>Click Close. You should now see new emulator images that support this preview
+  release of Google Play services and the Google Play services client libraries you need to
+  start developing.</li>
 </ol>
 
-
-
-<h2 style="margin-bottom: 0px;">3. Start Building</h2><hr>
 <p>Check out the <a href="/training/building-wearables">Building Apps for Wearables</a>
 training classes for information on how to build for Wear.</p>
     </div>
-
-   
-      
-
-  </div> <!-- end jd-content -->
+</div> <!-- end jd-content -->
 </div><!-- end doc-content -->
-</div> <!-- end body-content --> 
+</div> <!-- end body-content -->
 </body>
 </html>
\ No newline at end of file
diff --git a/docs/html/preview/images/opt-in.png b/docs/html/preview/images/opt-in.png
index 51754af..0f309c2 100644
--- a/docs/html/preview/images/opt-in.png
+++ b/docs/html/preview/images/opt-in.png
Binary files differ
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index a292146..41d918c 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -93,9 +93,9 @@
       <a href="<?cs var:toroot ?>preview/license.html">License Agreement</a>
       </div>
   </li>
-  <li class="nav-section" style="margin: 20px 0 0 3px;">
-    <div class="nav-section-header paging-links empty">
-      <a href="<?cs var:toroot ?>index.html" class="prev-page-link">Developer Home</a>
+  <li class="nav-section" style="margin: 20px 0 0 -10px;">
+    <div class="nav-section-header empty">
+      <a href="<?cs var:toroot ?>index.html" class="back-link">Developer Home</a>
       </div>
   </li>
 </ul>
diff --git a/docs/html/preview/samples.jd b/docs/html/preview/samples.jd
index 635f49e..e4e1674 100644
--- a/docs/html/preview/samples.jd
+++ b/docs/html/preview/samples.jd
@@ -3,31 +3,8 @@
 @jd:body
 
 <p>The following code samples are provided for the L Developer Preview. You can
-download them with the Android SDK Manager.</p>
-
-<p>To import a downloaded project:<p>
-
-<div class="toggle-content closed">
-<p style="margin-top:5px"><a href="#" onclick="return toggleContent(this)">
-  <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
-  />Using Android Studio</a></p>
-
-  <div class="toggle-content-toggleme">
-
-  <ol>
-    <li>Unpack the downloaded project package.</li>
-    <li>In <a href="{@docRoot}sdk/installing/studio.html">Android Studio</a>, chose
-    <strong>File > Import Project</strong> and select the root folder of
-    the unpacked project.
-      <p>Android Studio may ask you to choose the type of project you are
-        importing. If it does, make sure to choose <strong>Import project from
-        external model</strong> and select the <strong>Gradle</strong> option.
-      </p>
-    </li>
-  </ol>
-
-  </div>
-</div>
+download them in the Android SDK Manager under the <b>SDK Samples</b> component
+for the L Developer Preview.</p>
 
 <p class="note">
   <strong>Note:</strong> At this time, the downloadable projects are designed
@@ -35,8 +12,6 @@
 </p>
 
 
-
-
 <h3 id="BasicManagedProfile">BasicManagedProfile</h3>
 <div class="figure" style="width:220px">
   <img src="{@docRoot}preview/images/BasicManagedProfile.png"
@@ -85,7 +60,6 @@
 -->
 <p>This sample demonstrates how to record video using the Camera2 API.</p>
 
-
 <h3 id="ActivitySceneTransitionBasic">ActivitySceneTransitionBasic</h3>
 <div class="figure" style="width:220px">
   <img src="{@docRoot}preview/images/ActivitySceneTransitionBasic.png"
@@ -152,6 +126,14 @@
 </p>
 
 
+<div class="figure" style="width:220px">
+  <img src="{@docRoot}preview/images/JobSchedulerSample.png"
+      srcset="{@docRoot}preview/images/JobSchedulerSample@2x.png 2x"
+      alt="" height="375" />
+  <p class="img-caption">
+    <strong>Figure 3.</strong> The JobSchedulerSample sample app.
+  </p>
+</div>
 
 <h3 id="GameControllerSample">GameControllerSample</h3>
 <!--
@@ -201,14 +183,6 @@
 
 <h3 id="JobSchedulerSample">JobSchedulerSample</h3>
 
-<div class="figure" style="width:220px">
-  <img src="{@docRoot}preview/images/JobSchedulerSample.png"
-      srcset="{@docRoot}preview/images/JobSchedulerSample@2x.png 2x"
-      alt="" height="375" />
-  <p class="img-caption">
-    <strong>Figure 3.</strong> The JobSchedulerSample sample app.
-  </p>
-</div>
 
 <p>
 This sample app allows the user to schedule jobs through the UI, and shows
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 876b348..af466ab 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -1,6 +1,174 @@
 page.title=Setting Up the Preview SDK
 @jd:body
 
+
+
+
+
+
+
+<div style="position:relative; min-height:600px">
+
+  <div class="wrap" id="tos" style="position:absolute;display:none;width:inherit;">
+
+    <p class="sdk-terms-intro">Before installing the Android SDK, you must agree to the following terms and conditions.</p>
+
+      <h2 class="norule">Terms and Conditions</h2>
+    <div class="sdk-terms" onfocus="this.blur()" style="width:678px">
+This is the Android SDK Preview License Agreement (the “License Agreement”).
+
+1. Introduction
+
+1.1 The Android SDK Preview (referred to in the License Agreement as the “Preview” and specifically including the Android system files, packaged APIs, and Preview library files, if and when they are made available) is licensed to you subject to the terms of the License Agreement. The License Agreement forms a legally binding contract between you and Google in relation to your use of the Preview.
+
+1.2 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time.
+
+1.3 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.
+
+2. Accepting the License Agreement
+
+2.1 In order to use the Preview, you must first agree to the License Agreement. You may not use the Preview if you do not accept the License Agreement.
+
+2.2 By clicking to accept and/or using the Preview, you hereby agree to the terms of the License Agreement.
+
+2.3 You may not use the Preview and may not accept the License Agreement if you are a person barred from receiving the Preview under the laws of the United States or other countries including the country in which you are resident or from which you use the Preview.
+
+2.4 If you will use the Preview internally within your company or organization you agree to be bound by the License Agreement on behalf of your employer or other entity, and you represent and warrant that you have full legal authority to bind your employer or such entity to the License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the Preview on behalf of your employer or other entity.
+
+3. Preview License from Google
+
+3.1 Subject to the terms of the License Agreement, Google grants you a royalty-free, non-assignable, non-exclusive, non-sublicensable, limited, revocable license to use the Preview, personally or internally within your company or organization, solely to develop applications to run on the Android platform.
+
+3.2 You agree that Google or third parties owns all legal right, title and interest in and to the Preview, including any Intellectual Property Rights that subsist in the Preview. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you.
+
+3.3 You may not use the Preview for any purpose not expressly permitted by the License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the Preview or any part of the Preview; or (b) load any part of the Preview onto a mobile handset or any other hardware device except a personal computer, combine any part of the Preview with other software, or distribute any software or device incorporating a part of the Preview.
+
+3.4 You agree that you will not take any actions that may cause or result in the fragmentation of Android, including but not limited to distributing, participating in the creation of, or promoting in any way a software development kit derived from the Preview.
+
+3.5 Use, reproduction and distribution of components of the Preview licensed under an open source software license are governed solely by the terms of that open source software license and not the License Agreement. You agree to remain a licensee in good standing in regard to such open source software licenses under all the rights granted and to refrain from any actions that may terminate, suspend, or breach such rights.
+
+3.6 You agree that the form and nature of the Preview that Google provides may change without prior notice to you and that future versions of the Preview may be incompatible with applications developed on previous versions of the Preview. You agree that Google may stop (permanently or temporarily) providing the Preview (or any features within the Preview) to you or to users generally at Google's sole discretion, without prior notice to you.
+
+3.7 Nothing in the License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features.
+
+3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the Preview.
+
+4. Use of the Preview by You
+
+4.1 Google agrees that nothing in the License Agreement gives Google any right, title or interest from you (or your licensors) under the License Agreement in or to any software applications that you develop using the Preview, including any intellectual property rights that subsist in those applications.
+
+4.2 You agree to use the Preview and write applications only for purposes that are permitted by (a) the License Agreement, and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
+
+4.3 You agree that if you use the Preview to develop applications, you will protect the privacy and legal rights of users. If users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If users provide you with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, each user has given you permission to do so.
+
+4.4 You agree that you will not engage in any activity with the Preview, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of Google or any third party.
+
+4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so.
+
+4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under the License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.
+
+4.7 The Preview is in development, and your testing and feedback are an important part of the development process. By using the Preview, you acknowledge that implementation of some features are still under development and that you should not rely on the Preview having the full functionality of a stable release. You agree not to publicly distribute or ship any application using this Preview as this Preview will no longer be supported after the official Android SDK is released.
+
+5. Your Developer Credentials
+
+5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials.
+
+6. Privacy and Information
+
+6.1 In order to continually innovate and improve the Preview, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the Preview are being used and how they are being used. Before any of this information is collected, the Preview will notify you and seek your consent. If you withhold consent, the information will not be collected.
+
+6.2 The data collected is examined in the aggregate to improve the Preview and is maintained in accordance with Google's Privacy Policy located at http://www.google.com/policies/privacy/.
+
+7. Third Party Applications
+
+7.1 If you use the Preview to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources.
+
+7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners.
+
+7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party.
+
+8. Using Google APIs
+
+8.1 Google APIs
+
+8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service.
+
+8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so.
+
+9. Terminating the License Agreement
+
+9.1 the License Agreement will continue to apply until terminated by either you or Google as set out below.
+
+9.2 If you want to terminate the License Agreement, you may do so by ceasing your use of the Preview and any relevant developer credentials.
+
+9.3 Google may at any time, terminate the License Agreement, with or without cause, upon notice to you.
+
+9.4 The License Agreement will automatically terminate without notice or other action upon the earlier of:
+(A) when Google ceases to provide the Preview or certain parts of the Preview to users in the country in which you are resident or from which you use the service; and
+(B) Google issues a final release version of the Android SDK.
+
+9.5 When the License Agreement is terminated, the license granted to you in the License Agreement will terminate, you will immediately cease all use of the Preview, and the provisions of paragraphs 10, 11, 12 and 14 shall survive indefinitely.
+
+10. DISCLAIMERS
+
+10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE PREVIEW IS AT YOUR SOLE RISK AND THAT THE PREVIEW IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
+
+10.2 YOUR USE OF THE PREVIEW AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE PREVIEW IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. WITHOUT LIMITING THE FOREGOING, YOU UNDERSTAND THAT THE PREVIEW IS NOT A STABLE RELEASE AND MAY CONTAIN ERRORS, DEFECTS AND SECURITY VULNERABILITIES THAT CAN RESULT IN SIGNIFICANT DAMAGE, INCLUDING THE COMPLETE, IRRECOVERABLE LOSS OF USE OF YOUR COMPUTER SYSTEM OR OTHER DEVICE.
+
+10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+
+11. LIMITATION OF LIABILITY
+
+11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
+
+12. Indemnification
+
+12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys’ fees) arising out of or accruing from (a) your use of the Preview, (b) any application you develop on the Preview that infringes any Intellectual Property Rights of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you of the License Agreement.
+
+13. Changes to the License Agreement
+
+13.1 Google may make changes to the License Agreement as it distributes new versions of the Preview. When these changes are made, Google will make a new version of the License Agreement available on the website where the Preview is made available.
+
+14. General Legal Terms
+
+14.1 the License Agreement constitutes the whole legal agreement between you and Google and governs your use of the Preview (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the Preview.
+
+14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.
+
+14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of the License Agreement is invalid, then that provision will be removed from the License Agreement without affecting the rest of the License Agreement. The remaining provisions of the License Agreement will continue to be valid and enforceable.
+
+14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the License Agreement.
+
+14.5 EXPORT RESTRICTIONS. THE PREVIEW IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE PREVIEW. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
+
+14.6 The License Agreement may not be assigned or transferred by you without the prior written approval of Google, and any attempted assignment without such approval will be void. You shall not delegate your responsibilities or obligations under the License Agreement without the prior written approval of Google.
+
+14.7 The License Agreement, and your relationship with Google under the License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
+    </div><!-- sdk terms -->
+
+
+
+    <div id="sdk-terms-form">
+      <p>
+        <input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" />
+        <label id="agreeLabel" for="agree">I have read and agree with the above terms and conditions</label>
+      </p>
+      <p><a href="" class="button disabled" id="downloadForRealz" onclick="return onDownloadForRealz(this);"></a></p>
+    </div>
+
+
+  </div><!-- end TOS -->
+
+
+
+
+
+
+
+
+
+  <div id="landing">
+
 <p>The Preview SDK is available from the Android SDK Manager. <!-- Not yet! -->
 This document assumes that you are familiar with Android app development, such
 as using the Android SDK Manager and creating projects. If you're new to
@@ -52,26 +220,25 @@
 
 <ol>
   <li>Download and uncompress the Android Developer Preview package.
-    <p class="table-caption" id="table1">
-      <strong>Table 1.</strong> L Developer Preview system images.</p>
-    <table>
+    <table style="width:860px">
       <tr>
         <th scope="col">Device</th>
         <th scope="col">Download</th>
-        <th scope="col">MD5 Checksum</th>
-        <th scope="col">SHA-1 Checksum</th>
+        <th scope="col">Checksum</th>
       </tr>
       <tr id="hammerhead">
-        <td>Nexus 5 (GSM/LTE) "hammerhead"</td>
-        <td><!-- TODO --></td>
-        <td><code>5a6ae77217978cb7b958a240c2e80b57</code></td>
-        <td><code>ac1d8a8e4f4a1dca5864dc733caa940bffc28616</code></td>
+        <td>Nexus 5 (GSM/LTE) <br>"hammerhead"</td>
+        <td><a href="#top" onclick="onDownload(this)"
+          >hammerhead-lpv79-preview-ac1d8a8e.tgz</a></td>
+        <td>MD5: <code>5a6ae77217978cb7b958a240c2e80b57</code>
+        <br>SHA-1: <code>ac1d8a8e4f4a1dca5864dc733caa940bffc28616</code></td>
       </tr>
       <tr id="razor">
-        <td>Nexus 7 (Wifi) "razor"</td>
-        <td><!-- TODO --></td>
-        <td><code>b293a5d3a4e07beabebcc0be85ad68a2</code></td>
-        <td><code>d0ddf8ce733ba2a34279cdff8827fd604762c2342d</code></td>
+        <td>Nexus 7 (Wifi) <br>"razor"</td>
+        <td><a href="#top" onclick="onDownload(this)"
+          >razor-lpv79-preview-d0ddf8ce.tgz</a></td>
+        <td>MD5: <code>b293a5d3a4e07beabebcc0be85ad68a2</code>
+        <br><nobr>SHA-1: <code>d0ddf8ce733ba2a34279cdff8827fd604762c2342d</nobr></td>
       </tr>
     </table>
   </li>
@@ -142,3 +309,56 @@
     &lt;/style>
 &lt;/resources>
 </pre>
+
+  </div><!-- landing -->
+
+</div><!-- relative wrapper -->
+
+
+
+<script>
+  var urlRoot = "http://storage.googleapis.com/androiddevelopers/preview/";
+  function onDownload(link) {
+
+    $("#downloadForRealz").html("Download " + $(link).text());
+    $("#downloadForRealz").attr('href', urlRoot + $(link).text());
+
+    $("#tos").fadeIn('fast');
+    $("#landing").fadeOut('fast');
+
+    return true;
+  }
+
+
+  function onAgreeChecked() {
+    /* verify that the TOS is agreed */
+    if ($("input#agree").is(":checked")) {
+      /* reveal the download button */
+      $("a#downloadForRealz").removeClass('disabled');
+    } else {
+      $("a#downloadForRealz").addClass('disabled');
+    }
+  }
+
+  function onDownloadForRealz(link) {
+    if ($("input#agree").is(':checked')) {
+      $("#tos").fadeOut('fast');
+      $("#landing").fadeIn('fast');
+      _gaq.push(['_trackEvent', 'L Preview', 'System Image', $("#downloadForRealz").html()]);
+      location.hash = "";
+      return true;
+    } else {
+      $("label#agreeLabel").parent().stop().animate({color: "#258AAF"}, 200,
+        function() {$("label#agreeLabel").parent().stop().animate({color: "#222"}, 200)}
+      );
+      return false;
+    }
+  }
+
+  $(window).hashchange( function(){
+    if (location.hash == "") {
+      location.reload();
+    }
+  });
+
+</script>
\ No newline at end of file
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
index b9de3a4..763eada 100644
--- a/docs/html/preview/tv/games/index.jd
+++ b/docs/html/preview/tv/games/index.jd
@@ -1,70 +1,152 @@
-page.title=Games on TV
+ikpage.title=Games on TV
 page.tags="controller"
 
 @jd:body
 
-<p>This section complements the [larger best-practices guidance for designing for Android TV](TODO, use formal name of referenced doc, and add link). It assumes that you have read that guidance, and seeks to minimize repetition.</p>
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#display">Display</li></a></li>
+    <li><a href="#control">Input Devices</li></a></li>
+    <li><a href="#manifest">Manifest</li></a></li>
+    <li><a href="#gpgs">Google Play Game Services</li></a></li>
+    <li><a href="#web">Web</a></li>
+  </ol>
+</div>
+</div>
 
-<h2>Overview</h2>
-<p>Because of factors including its large size, its control scheme, and its nature as a shared display, the television screen presents a number of considerations that may be new to mobile developers. This document breaks these considerations down into five sections:</p>
+<p>The television screen presents a number of considerations that may be new to mobile-game
+developers. These areas include its large size, its control scheme, and the fact that all
+players are viewing it simultaneously.</p>
+
+
+<h2 id=display>Display</h2>
+<p>The two main things to keep in mind when developing games for the TV screen are its nature as a
+shared display and the need to design your game for a landscape orientation.</p>
+<h3>Shared display</h3>
+<p>A living-room TV poses design challenges for multiplayer games, in that all players can see
+everything. This issue is especially relevant to games (such as card games or strategy games) that
+rely on each player’s possession of hidden information.</p>
+<p>Some mechanisms you can implement to address the problem of one player’s eavesdropping
+on another’s information are:</p>
 <ul>
-<li>Display</li>
-<li>Control</li>
-<li>Manifest</li>
-<li>Google Play Game Services</li>
-<li>Web</li>
+<li>A blinder on the screen to help conceal information. For example, in a
+turn-based game like a word or card game, one player at a time might view the display. When the
+player finishes a move, the game allows him or her to cover the screen with a blinder that
+blocks anyone from viewing secret information. When the next player begins a turn, the blinder
+opens to reveal his or her own information.</li>
+<li>A companion app, running on a phone or tablet, can enable a player to conceal
+information.</li>
 </ul>
-<h2>Display</h2>
-<p>Large and centrally situated, the television screen imposes limitations, but also opens up new opportunities for immersive gameplay.</p>
-<h3>A shared display</h3>
-<p>A living-room TV poses design challenges for multiplayer games, in that all players can see everything. This issue is especially germane to games (such as card games or strategy games) that rely on each player’s possession of hidden information.</p>
-<p>Some mechanisms you can implement to address the problem of one player’s “eavesdropping” on another’s information are:</p>
-<ul>
-<li>A player might place a "blinder" on the screen to help conceal information. For example, in a turn-based game like a word or card game, one player at a time might view the display. When the player finishes a move, the game allows him or her to cover the screen with a “blinder” that blocks anyone from viewing secret information. When the next player begins a turn, the blinder opens to reveal his or her own information.</li>
-<li>A second screen, such as a handset or larger device, can enable a player to conceal information. For information on implementing second-screen support, see <a href="http://developer.android.com/reference/android/app/Presentation.html">Presentation</a> on the Android developer site.</li>
-</ul>
-<h3>No touch interface</h3>
-<p>A television does not have a touch interface. Your game design, therefore, need not take into account the possibility that a player’s controlling fingers might block the on-screen action. You can assume constant visibility of the entire viewing area.</p>
-<p>See the <a href=#control>Control</a> section in this document and in [Design for TV](TODO, use formal name of referenced doc, and add link) for more implications of the lack of touch interface.</p>
 <h3>Landscape display</h3>
-<p>In mobile-device terms, a TV is always “sideways.” You can’t turn it, and there is no portrait orientation. You should always be designing your TV games to be displayed in landscape mode.</p>
-<a id=control><h2>Control</h2>
-<p>Without a touch interface, it's even more important than usual to get your controls right, so that players find them intuitive and fun to use. The separation of controller from device also introduces some other issues to pay attention to, like keeping track of multiple players' controllers, and handling disconnects gracefully.</p>
+<p>A TV is always sideways: You can’t turn it, and there is no
+portrait orientation. Always design your TV games to be displayed in landscape
+mode.</p>
+
+<h2 id="control">Input Devices</h2>
+<p>TVs don't have touch interfaces, so it's even more important to get your controls right and make
+ sure that players find them intuitive and fun to use. The separation of controller from device also
+introduces some other issues to pay attention to, like keeping track of multiple players'
+controllers, and handling disconnects gracefully.</p>
 <h3>D-pad</h3>
-<p>Because of the lack of touch interface, you should be planning your control scheme based on a D-pad. Some key points to keep in mind include:</p>
-<p>The player needs to use the gamepad in all aspects of the game&ndash;not just controlling core gameplay, but also navigating menus and ads. For this reason, you should also ensure that your Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a player to "Tap to skip".</p>
-<p>You can avoid unhappy surprises (and resulting low ratings) by using your Play Store description to communicate to the player any expectations about controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad, you should make this clear. A player who uses an ill-suited controller for a game is likely to have a subpar experience&ndash;and penalize your game in the ratings.</p>
-<p>You can also help ensure a good player experience by ensuring that button mapping is intuitive and flexible. For example, you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to <code>Cancel</code>. You can also offer flexibility in the form of remappability. For more information on button mapping, see <a href="http://developer.android.com/training/game-controllers/controller-input.html">Handling Controller Actions</a>.</p>
-<p>Your game can also contribute to a good match between controller and game by querying the controller about its capabilities. For example, you may intend for a player to steer an object by waving the controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however, waving will not work. But when your game queries the controller and discovers that motion detection is not supported, it can switch over to an alternative, available control scheme.</p>
-<p>For more information on querying controller capabilities, see <a href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting Controllers Across Android Versions</a>.</p>
+<p>Plan your control scheme around a directional pad (D-pad) control, since this control set is the
+default for Android TV devices. The player needs to be able to use a D-Pad in all aspects of the
+game&ndash;not just controlling core
+gameplay, but also navigating menus and ads. For this reason, you should also ensure that your
+Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a
+player to <strong>Tap to skip</strong>.</p>
+<p>How you shape the player's interaction with the controller can be key to achieving a great user
+experience:
+  <ul>
+  <p><li><strong>Communicate Controller Requirements up Front</strong> - Use your Play Store description to communicate to the player any expectations about
+controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad,
+make this fact clear. A player who uses an ill-suited controller for a game is likely to have a
+subpar experience&ndash;and penalize your game in the ratings.</p>
+  <p><li><strong>Use Consistent Button Mapping</strong> - Intuitive and flexible button mapping is key to a good user experience. For example,
+you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to
+<code>Cancel</code>. You can also offer flexibility in the form of remappability. For more
+information on button mapping, see <a
+href="http://developer.android.com/training/game-controllers/controller-input.html">Handling
+Controller Actions</a>.</p>
+  <p><li><strong>Detect Controller Capabilities and Adjust Accordingly</strong> - Query the controller about its capabilities in order to optimize the match between
+controller and game. For example, you may intend for a player to steer an object by waving the
+controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however,
+waving will not work. When, however, your game queries the controller and discovers that motion detection
+is not supported, it can switch over to an alternative, available control scheme.
+For more information on querying controller capabilities, see <a
+href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting
+Controllers Across Android Versions</a>.</p>
+  </ul>
 <h3>Back-button behavior</h3>
-<p>The Back button should never act as a toggle. For example, do not use it to both open and close a menu. Its behavior should only be linear. For example: Game play &gt; Game pause screen &gt; Game main screen &gt; Android home screen.</p>
-<p>With this principle of "linear navigation" in mind, you <b>may</b> use the back button to leave an in-game menu (opened by a different button) and return to gameplay.</p>
+<p>The Back button should never act as a toggle. For example, do not use it to both open and close
+a menu. It should only navigate backward, breadcrumb-style, through the previous screens the player has
+been on. For example: Game play &gt; Game pause screen &gt; Game
+main screen &gt; Android home screen.</p>
+<p>Since the Back button should only perform linear (backward) navigation, you may use the
+back button to leave an in-game menu (opened by a different button) and return to gameplay. For
+more information about design for navigation, see <a
+href="http://developer.android.com/design/patterns/navigation.html">Navigation with Back and
+Up</a>. To learn about implementation, refer to <a
+href="http://developer.android.com/training/implementing-navigation/temporal.html">Providing Proper
+Back Navigation</a>. </p>
 <h3>Handling multiple controllers</h3>
-<p>When multiple players are playing a game, each with his or her own controller, it is important to map each player-controller pair. For information on how to implement controller-number identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
+<p>When multiple players are playing a game, each with his or her own controller, it is important
+to map each player-controller pair. For information on how to implement controller-number
+identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html
+#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
 <h3>Handling disconnects</h3>
-<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog should appear prompting the disconnected player to reconnect his or her controller.</p>
-<p>The dialog should also offer troubleshooting tips (e.g., "Check your Bluetooth connection").</p>
-<h2>Manifest</h2>
-<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the <code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a value of either <code>true</code> or <code>false</code>. For example:</p>
+<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog
+should appear prompting the disconnected player to reconnect his or her controller.</p>
+<p>The dialog should also offer troubleshooting tips (for example, a pop-up dialog telling the player to
+"Check your Bluetooth connection"). For more information on implementing input-device support, see <a
+href="http://developer.android.com/training/game-controllers/controller-input.html">Supporting Game
+Controllers"</a>. Specific information about Bluetooth connections is at <a
+href="http://developer.android.com/guide/topics/connectivity/bluetooth.html">Bluetooth</a>.</p>
+
+<h2 id="manifest">Manifest</h2>
+<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the
+<code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a
+value of either <code>true</code> or <code>false</code>. For example:</p>
 <pre class="fragment">&lt;application&gt;
- . . .
- &lt;meta-data android:name="isGame" android:value=["true" | "false"]/&gt;
-android:isGame=["true" | "false"] &gt;
- . . .
-&lt;/application&gt;
-</pre><h2>Google Play Game Services</h2>
-<p>If your game integrates Google Play Game Services, you should keep in mind a number of considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
+ ...
+&lt; android:isGame=["true" | "false"] &gt;
+ ...
+&lt;/application&gt;</pre>
+
+<h2 id="gpgs">Google Play Game Services</h2>
+<p>If your game integrates Google Play Game Services, you should keep in mind a number of
+considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
 <h3>Achievements</h3>
-<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay from a supported input device should be able to earn achievements.</p>
-<h3>Sign-on</h3>
-<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several times in a row, your game should stop asking.</p>
+<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay
+from a supported input device should be able to earn achievements. For more information on
+Achievements and how to implement them, see <a
+href="https://developers.google.com/games/services/android/achievements">Achievements in
+Android</a>.</p>
+<h3>Sign-in</h3>
+<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several
+times in a row, your game should stop asking. Learn more about sign-in at <a
+href="https://developers.google.com/games/services/training/signin">Implementing Sign-in on
+Android</a>.</p>
 <h3>Saving</h3>
-<p>We highly recommend using Play Services cloud save to store your game save. Your game should bind game saves to a specific Google account, so as to be uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game should be able to pull the same game-save information from his or her account.</p>
-<p>You should also provide an option in your game's UI to prompt the player to destroy save data. You might put the option in the game's <code>Settings</code> screen.</p>
+<p>We highly recommend using Play Services <a
+href="https://developers.google.com/games/services/common/concepts/cloudsave">Cloud Save</a> to
+store your game save. Your game should bind game saves to a specific Google account, so as to be
+uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game
+should be able to pull the same game-save information from his or her account.</p>
+<p>You should also provide an option in your game's UI to allow the player to delete locally and
+cloud-stored data. You might put the option in the game's <code>Settings</code> screen. For
+specifics on implementing Cloud Save, see <a
+href="https://developers.google.com/games/services/android/cloudsave">Cloud Save in Android</a>.</p>
 <h3>Multiplayer experience</h3>
-<p>A game offering a multiplayer experience must allow at least two players to enter a room.</p>
-<h2>Web</h2>
-<p>Android TV games do not support a full web browser. You should therefore avoid using generic URLs in your game.</p>
-<p>Webviews will work for logins to services like Google+ and Facebook. </p>
+<p>A game offering a multiplayer experience must allow at least two players to enter a room. For
+further information on multiplayer games in Android, see the <a
+href="https://developers.google.com/games/services/android/realtimeMultiplayer">Real-time
+Multiplayer</a> and <a href="">Turn-based Multiplayer</a> documentation on the Android developer
+site.</p>
+
+<h2 id="web">Web</h2>
+<p>We discourage including web browsing in games for Android TV. The television set is not well-suited for browsing,, either in terms of display or control scheme.</p>
+<p class="note"><strong>Note:</strong> You can use the {@link android.webkit.WebView} class for logins to services like Google+ and
+Facebook. </p>
 
diff --git a/docs/html/preview/tv/start/index.jd b/docs/html/preview/tv/start/index.jd
index 11d6ad3..b75fee0 100644
--- a/docs/html/preview/tv/start/index.jd
+++ b/docs/html/preview/tv/start/index.jd
@@ -153,8 +153,7 @@
 </p>
 
 <p>If you decide to use the v17 leanback library for your app, you should note that it is
-  dependent on the <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
-  appcompat library</a>, which is, in turn, dependent on the
+  dependent on the
   <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. This means
   that apps that use the leanback support library should include all of these support
   libraries:</p>
@@ -162,12 +161,11 @@
 <ul>
   <li>v17 leanback support library</li>
   <li>v7 recyclerview support library</li>
-  <li>v7 appcompat support library</li>
   <li>v4 support library</li>
 </ul>
 
-<p>Two of these libraries (v17 leanback and v7 appcompat) contain resources, which require
-  you to take specific steps to include them in app projects. For instructions on
+<p>The v17 leanback library contain resources, which requires
+  you to take specific steps to include it in app projects. For instructions on
   importing a support library with resources, see
   <a href="http://developer.android.com/tools/support-library/setup.html#libs-with-res">
   Support Library Setup</a>.
diff --git a/docs/html/preview/tv/ui/browse.jd b/docs/html/preview/tv/ui/browse.jd
index d7a1fb6..d6b97c1 100644
--- a/docs/html/preview/tv/ui/browse.jd
+++ b/docs/html/preview/tv/ui/browse.jd
@@ -152,10 +152,9 @@
   image as users browse through content. This technique can make interaction with your app feel more
   cinematic and enjoyable for users.</p>
 
-<p>The Leanback support library provides a {@link
-  android.support.v17.leanback.app.BackgroundManager} class for changing the background of your TV
-  app activity. The following example shows how to create a simple method for updating the
-  background within your TV app activity:</p>
+<p>The Leanback support library provides a {@code BackgroundManager} class for changing the
+  background of your TV app activity. The following example shows how to create a simple method
+  for updating the background within your TV app activity:</p>
 
 <pre>
 protected void updateBackground(Drawable drawable) {
@@ -166,7 +165,7 @@
 <p>Many of the existing media-browse apps automatically update the background as the user
   navigates through media listings. In order to do this, you can set up a selection listener to
   automatically update the background based on the user's current selection. The following example
-  shows you how to set up an {@link android.support.v17.leanback.widget.OnItemSelectedListener}
+  shows you how to set up an {@code OnItemSelectedListener}
   class to catch selection events and update the background:</p>
 
 <pre>
diff --git a/docs/html/training/building-wearables.jd b/docs/html/training/building-wearables.jd
new file mode 100644
index 0000000..4fda10e
--- /dev/null
+++ b/docs/html/training/building-wearables.jd
@@ -0,0 +1,8 @@
+page.title=Building Apps for Wearables
+page.trainingcourse=true
+
+@jd:body
+
+
+<p>These classes teach you how to build notifications in a handheld app that are automatically
+synced to wearables as well as how to build apps that run on wearables.</p>
\ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index c5dc3c5..4407d30 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -638,7 +638,6 @@
             </li>
         </ul>
       </li>
-
     </ul>
   </li>
   <!-- End connectivity and cloud -->
@@ -727,6 +726,90 @@
   <!-- End privacy and location -->
 
 
+  <li class="nav-section">
+    <div class="nav-section-header">
+      <a href="<?cs var:toroot ?>training/building-wearables.html">
+      <span class="small">Building Apps for</span><br/>
+              Wearables
+      </a>
+    </div>
+    <ul>
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/wearables/notifications/index.html"
+             description="How to build handheld notifications that are synced to
+             and look great on wearables."
+            >Adding Wearable Features to Notifications</a>
+        </div>
+        <ul>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/notifications/creating.html">Creating a Notification</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/notifications/voice-input.html">Receiving Voice Input in a Notification</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/notifications/pages.html">Adding Pages to a Notification</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/notifications/stacks.html">Stacking Notifications</a>
+          </li>
+        </ul>
+      </li>
+
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/wearables/apps/index.html"
+             description="How to build apps that run directly on wearables."
+            >Creating Wearable Apps</a>
+        </div>
+        <ul>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/apps/creating.html">Creating and Running a Wearable App</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/apps/layouts.html">Creating Custom Layouts</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/apps/voice.html">Adding Voice Capabilities</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/apps/packaging.html">Packaging Wearable Apps</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/apps/bt-debugging.html">Debugging over Bluetooth</a>
+          </li>
+        </ul>
+      </li>
+
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/wearables/data-layer/index.html"
+             description="How to sync data between handhelds and wearables."
+            >Sending and Syncing Data</a>
+        </div>
+        <ul>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/data-layer/accessing.html">Accessing the Wearable Data Layer</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/data-layer/data-items.html">Syncing Data Items</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/data-layer/assets.html">Transferring Assets</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/data-layer/messages.html">Sending and Receiving Messages</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/wearables/data-layer/events.html">Handling Data Layer Events</a>
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </li>
+
+  <!-- End Building for wearables -->
 
   <li class="nav-section">
     <div class="nav-section-header">
diff --git a/docs/html/training/wearables/apps/bt-debugging.jd b/docs/html/training/wearables/apps/bt-debugging.jd
new file mode 100644
index 0000000..8d09c43
--- /dev/null
+++ b/docs/html/training/wearables/apps/bt-debugging.jd
@@ -0,0 +1,92 @@
+page.title=Debugging over Bluetooth
+
+@jd:body
+
+<div id="tb-wrapper">
+  <div id="tb">
+
+    <!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+    <h2>This lesson teaches you to</h2>
+    <ol>
+      <li><a href="#SetupDevices">Set Up Devices for Debugging</a></li>
+      <li><a href="#SetupSession">Set Up a Debugging Session</a></li>
+      <li><a href="#DebugApp">Debug Your App</a></li>
+    </ol>
+    <h2>You should also read</h2>
+    <ul>
+      <li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
+    </ul>
+  </div>
+</div>
+
+<p>You can debug your wearable over Bluetooth by routing it's debug output to the
+handheld device that's connected to your development machine.</p>
+
+<h2 id="SetupDevices">Setup Devices for Debugging</h2>
+<ol>
+  <li>Enable USB debugging on the handheld:
+    <ul>
+      <li>Open the Settings app and scroll to the bottom.</li>
+      <li>If it doesn't have a Developer Options setting, tap <b>About Phone</b>
+      (or <b>About Tablet</b>), scroll to the bottom, and tap the build number 7 times.</li>
+      <li>Go back and tap <b>Developer Options</b>.</li>
+      <li>Enable <b>USB debugging</b>.</li>
+    </ul>
+  </li>
+  <li>Enable Bluetooth debugging on the wearable:
+    <ol>
+      <li>Tap the home screen twice to bring up the Wear menu. </li>
+      <li>Scroll to the bottom and tap <b>Settings</b>.</li>
+      <li>Scroll to the bottom. If there's no <b>Developer Options</b> item, tap <b>About</b>,
+      and then tap the build number 7 times.</li>
+      <li>Tap the <b>Developer Options</b> item.</li>
+      <li>Enable <b>Debug over Bluetooth</b>.</li>
+    </ol>
+  </li>
+</ol>
+
+<h2 id="SetupSession">Set Up a Debugging Session</h2>
+<ol>
+ <li>On the handheld, open the Android Wear companion app.</li>
+ <li>Tap the menu on the top right and select <b>Settings</b>.</li>
+ <li>Enable <b>Debugging over Bluetooth</b>. You should see a tiny status summary appear under the
+ option:
+ <pre>
+Host: disconnected
+Target: connected
+</pre>
+</li>
+<li>Connect the handheld to your machine over USB and run:
+<pre>
+adb forward tcp:4444 localabstract:/adb-hub; adb connect localhost:4444
+</pre>
+
+<p class="note"><b>Note</b>: You can use any available port that you have access to.</p>
+</li>
+</ol>
+<p>
+In the Android Wear companion app, you should see the status change to:</p>
+<pre>
+Host: connected
+Target: connected
+</pre>
+
+<h2 id="#debug">Debug Your App</h2>
+
+Your wearable should show up as <code>localhost:4444</code> when running <code>adb devices</code>.
+
+To run any <code>adb</code> command, use this format:
+
+<pre>adb -s localhost:4444 &lt;command&gt; </pre>
+
+<p>If there are no other devices connected over TCP/IP (namely emulators),  you can shorten the command
+to:</p>
+<pre>
+adb -e &lt;command&gt;
+</pre>
+<p>For example:</p>
+<pre>
+adb -e logcat
+adb -e shell
+adb -e bugreport
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/wearables/apps/creating.jd b/docs/html/training/wearables/apps/creating.jd
new file mode 100644
index 0000000..145aed8
--- /dev/null
+++ b/docs/html/training/wearables/apps/creating.jd
@@ -0,0 +1,184 @@
+page.title=Creating and Running a Wearable App
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#SetupEmulator">Set Up an Android Wear Emulator</a></li>
+  <li><a href="#SetupDevice">Set Up an Android Wear Device</a></li>
+  <li><a href="#CreateProject">Create a Project</a></li>
+  <li><a href="#Libraries">Include the Correct Libraries</a></li>
+</ol>
+<h2>Dependencies and Prerequisites</h2>
+  <ul>
+    <li>Android Studio 0.8 or later and Gradle 1.12 or later</li>
+  </ul>
+</div>
+</div>
+
+<p>Wearable apps run directly on the wearable device, giving you access to low-level
+hardware such as sensors, activities, services, and more, right
+on the wearable.</p>
+
+<p>A companion handheld app that contains the
+wearable app is also required when you want to publish to the Google Play store.
+Wearables don't support the Google Play store, so users download the companion handheld app,
+which automatically pushes the wearable app to the wearable. The handheld app is also
+useful for doing heavy processing, network actions, or other work and
+sending the results to the wearable.
+</p>
+
+<p>This lesson goes over how to set up a device or emulator and create one project to contain
+both your wearable and handheld apps.
+</p>
+
+
+<h2 id="SetupEmulator">Set Up an Android Wear Emulator or Device</h2>
+<p>We recommend that you develop on real hardware so you can better
+gauge the user experience. However, the emulator lets you test out different
+types of screen shapes, which is useful for testing.</p>
+
+<h3>Set up an Android Wear Virtual Device</h3>
+
+<p>To set up an Android Wear virtual device:</p>
+<ol>
+  <li>Click <b>Tools > Android > AVD Manager</b>.</li>
+  <li>Click <b>Create...</b>.</li>
+  <li>Fill in the following details for the AVD you want to specify and leave the rest
+  of the fields with their default values:
+    <ol>
+      <li><b>AVD Name</b> - A name for your AVD</li>
+      <li><b>Device</b> - Android Wear Round or Square device types</li>
+      <li><b>Target</b> - Android 4.4W - API Level 20</li>
+      <li><b>CPU/ABI</b> - Android Wear ARM (armeabi-v7a)</li>
+      <li><b>Keyboard</b> - Select <b>Hardware keyboard present</b></li>
+      <li><b>Skin</b> - AndroidWearRound or AndroidWearSquare depending on the selected device type</li>
+      <li><b>Snapshot</b> - Not selected</li>
+      <li><b>Use Host GPU</b> - Selected, to support custom activities for wearable notifications</li>
+    </ol>
+  </li>
+  <li>Click <b>OK</b>.</li>
+<li>Start the emulator:
+<ol>
+  <li>Select the virtual device you just created.</li>
+  <li>Click <b>Start...</b>, then click <b>Launch</b>.</li>
+  <li>Wait until the emulator initializes and shows the Android Wear home screen.</li>
+</ol>
+</li>
+<li>Pair Your handheld with the emulator:
+<ol>
+  <li>On your handheld, install the Android Wear app from Google Play.</li>
+  <li>Connect the handheld to your machine through USB.</li>
+  <li>Forward the AVD's communication port to the connected handheld device (you must
+  do this every time the handheld is connected):
+  <pre>adb -d forward tcp:5601 tcp:5601</pre>
+  </li>
+  <li>Start the Android Wear app on your handheld device and connect to the emulator.</li>
+  <li>Tap the menu on the top right corner of the Android Wear app and select
+  <b>Demo Cards</b>.</li>
+  <li>The cards you select appear as notifications on the home screen of the emulator.</li>
+</ol>
+</li>
+</ol>
+
+<h3 id="SetupDevice">Set Up an Android Wear Device</h3>
+<p>To set up an Android Wear device:</p>
+<ol>
+  <li>Install the Android Wear app, available on Google Play, on your handheld.</li>
+  <li>Follow the app's instructions to pair your handheld with your wearable.
+  This allows you to test out synced handheld notifications, if you're building them.</li>
+  <li>Connect the wearable to your machine through USB, so you can install apps directly to it
+  as you develop.</li>
+</ol>
+
+<h2 id="CreateProject">Create a Project</h2>
+
+<p>To begin development, create an app project that contains
+ wearable and handheld app modules. In Android Studio, click <b>File</b> >
+ <b>New Project</b> and follow the Project Wizard instructions, as described in
+ <a href="{@docRoot}sdk/installing/create-project.html">Creating a
+Project</a>. As you follow the wizard, enter the following information:</p>
+
+<ol>
+  <li>In the <b>Configure your Project</b> window, enter a name for your app and a package
+  name.</li>
+  <li>In the <b>Form Factors</b> window:
+    <ul>
+      <li>Select <b>Phone and Tablet</b> and select <b>API 8: Android 2.2 (Froyo)</b>
+      under <b>Minimum SDK</b>.</li>
+      <li>Select <b>Wear</b> and select <b>API 20: Android 4.4 (KitKat Wear)</b>
+      under <b>Minimum SDK</b>.</li>
+    </ul>
+  </li>
+  <li>In the first <b>Add an Activity</b> window, add a blank activity for mobile.</li>
+  <li>In the second <b>Add an Activity</b> window, add a blank activity for Wear.
+
+  <p>When the wizard completes, Android Studio creates a new project with two modules, <b>mobile</b> and
+  <b>wear</b>. You now have a project for both your handheld and wearable apps that you can create activities,
+  services, custom layouts, and much more in. On the handheld app, you do most of the heavy lifting,
+  such as network communications, intensive processing, or tasks that require long
+  amounts of user interaction. When these are done,
+  you usually notify the wearable of the results through notifications or by syncing and sending
+  data to the wearable.</p>
+
+  <p class="note"><b>Note:</b> The <b>wear</b> module also contains a "Hello World" activity that uses a
+  <code>WatchViewStub</code> that inflates a layout based on whether the device's screen
+  is round or square. The <code>WatchViewStub</code> class is one of the UI widgets that's provided
+  by the <a href="{@docRoot}training/wearables/apps/layouts#UiLibrary">wearable support library</a>.</p>
+</li>
+
+<h2 id="Install">Install the Wearable app</h2>
+
+<p>When developing, you install apps directly to the wearable like with handheld apps. Use
+either <code>adb install</code> or the <b>Play</b> button on Android Studio.</p>
+
+<p>When you're ready to publish your app to users, you embed the wearable app inside of the
+handheld app. When users install the handheld app from Google Play, a connected wearable automatically
+receives the wearable app.</p>
+
+<p class="note"><b>Note:</b> The automatic installation of wearable apps
+does not work when you are signing apps with a debug key and only works with release keys. See
+<a href="{@docRoot}traiing/wearables/packaging.html">Packaging Wearable Apps</a> for
+complete information on how to properly package wearable apps.</p>
+
+<li>
+To install the "Hello World" app to the wearable, select <b>wear</b> from the <b>Run/Debug
+configuration</b> drop-down menu and click the <b>Play</b> button. The activity shows up on the
+wearable and prints out "Hello world!"
+</li></ol>
+<h2 id="Libraries">Include the Correct Libraries</h2>
+
+<p>As part of the Project Wizard, the correct
+dependencies are imported for you in the appropriate module's <code>build.gradle</code> file.
+However, these dependencies are not required, so read the following descriptions to find out if you need them or not:</p>
+
+<b>Notifications</b>
+<p>The <a href="{@docRoot}tools/support-library/features.html#v4">The
+Android v4 support library</a> (or v13, which includes v4)
+contains the APIs to extend your existing notifications on handhelds to support wearables.</p>
+
+<p>For notifications that appear only on
+the wearable (meaning, they are issued by an app that runs on the wearable), you can just use the
+standard framework APIs (API Level 20) on the wearable and remove the support library
+dependency in the <b>mobile</b> module of your project.
+</p>
+
+<b>Wearable Data Layer</b>
+<p>To sync and send data between wearables and handhelds with the Wearable Data Layer APIs,
+you need the latest version of
+<a href="{@docRoot}google/play-services/setup.html">Google Play services</a>.
+If you're not using these APIs, remove the dependency from both modules.</p>
+
+<b>Wearable UI support library</b>
+<p>This is an unofficial library that includes UI widgets designed for wearables. We encourage you
+to use them in your apps, because they exemplify best practices, but they can still
+change at any time. However, if the libraries are updated, your apps won't break since they are compiled
+into your app. To get new features from an updated library, you just need to
+statically link the new version and update your app accordingly.
+This library is only applicable if you create wearable apps.
+</p>
+
+<p>In the next lessons, you'll learn how to create layouts designed for wearables as well as how
+to use the various voice actions that are supported by the platform.</p>
diff --git a/docs/html/training/wearables/apps/index.jd b/docs/html/training/wearables/apps/index.jd
new file mode 100644
index 0000000..c2c7f39
--- /dev/null
+++ b/docs/html/training/wearables/apps/index.jd
@@ -0,0 +1,72 @@
+page.title=Creating Wearable Apps
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>Dependencies and Prerequisites</h2>
+  <ul>
+    <li>Android Studio 0.8 or later and Gradle 1.12 or later</li>
+  </ul>
+</div>
+</div>
+
+<p>
+Wearable apps run directly on the device, giving you access to hardware such as sensors and the
+GPU. They are fundamentally the same as apps built for other devices using the Android SDK, but
+differ greatly in design and usability and the amount of functionality provided.
+These are the main differences between handheld and wearable apps:</p>
+
+<ul>
+  <li>The system enforces a timeout period. If you are displaying an activity and user's don't
+  interact with it, the device sleeps. When it wakes back up, the Wear home screen is displayed
+  instead of your activity. If you need to show something persistent, create a notification in the
+  context stream instead.</li>
+  <li>Wearable apps are relatively small in size and functionality compared to handheld apps.
+  They contain only what makes sense on the wearable, which is usually a small
+  subset of the corresponding handheld app. In general, you should carry out operations on the
+  handheld when possible and send the results to the wearable.
+  </li>
+  <li>Users don't download apps directly onto the wearable. Instead, you bundle
+  the wearable app inside the handheld app. When users install the handheld app,
+  the system automatically installs the wearable app. However, for development
+  purposes, you can still install the wearable app directly to the wearable.</li>
+  <li><p>Wearable apps can access much of the standard Android APIs, but don't support
+  the following APIs:</p>
+  <ul>
+    <li>{@link android.webkit}</li>
+    <li>{@link android.print}</li>
+    <li>{@link android.app.backup}</li>
+    <li>{@link android.appwidget}</li>
+    <li>{@link android.hardware.usb}</li>
+  </ul>
+  <p>
+  You can check if a wearable supports a feature by calling
+  {@link android.content.pm.PackageManager#hasSystemFeature hasSystemFeature()}
+  before trying to use an API.</p>
+</li>
+</ul>
+
+<p class="note"><b>Note:</b> We recommend using Android Studio for Android Wear development
+as it provides project setup, library inclusion, and packaging conveniences that aren't available
+in ADT. The rest of this training assumes you're using Android Studio.
+</p>
+
+<h2>Lessons</h2>
+  <dl>
+    <dt><a href="{@docRoot}training/wearables/apps/creating.html">Creating and Running a Wearable App</a></dt>
+      <dd>Learn how to create an Android Studio project that
+      contains both the wearable and handheld app modules and how to run the app on a device
+      or emulator.</dd>
+    <dt><a href="{@docRoot}training/wearables/apps/activity.html">Creating Custom Layouts</a></dt>
+      <dd>Learn how to create and display custom layouts for notifications and
+      activities.</dd>
+    <dt><a href="{@docRoot}training/wearables/apps/voice.html">Adding Voice Capabilities</a></dt>
+      <dd>Learn how to launch an activity with a voice actions and how to start the
+      system speech recognizer app to obtain free-form voice input.</dd>
+    <dt><a href="{@docRoot}training/wearables/apps/packaging.html">Packaging Wearable Apps</a></dt>
+      <dd>Learn how to package a wearable app inside a
+      handheld app. This allows the system to install the wearable app automatically when
+      users install the companion handheld app from the Google Play store.</dd>
+    <dt><a href="{@docRoot}training/wearables/apps/packaging.html">Debugging over Bluetooth</a></dt>
+      <dd>Learn how to debug your wearable over Bluetooth instead of USB.</dd>
+  </dl>
\ No newline at end of file
diff --git a/docs/html/training/wearables/apps/layouts.jd b/docs/html/training/wearables/apps/layouts.jd
new file mode 100644
index 0000000..983c359
--- /dev/null
+++ b/docs/html/training/wearables/apps/layouts.jd
@@ -0,0 +1,131 @@
+page.title=Creating Custom Layouts
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#CustomNotifications">Create custom notifications</a></li>
+  <li><a href="#UiLibrary">Create Layouts with the Wearable UI Library</li>
+</ol>
+
+<!--STOPSHIP: link these -->
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>Creating layouts for wearables is the same as handheld devices, except you have to design
+for the screen size and for glanceability. Do not port functionality
+and the UI from a handheld app and expect a good experience. You should create custom layouts
+only when necessary. Read the <a href="{@docRoot}design/wear/index.html">design guidelines</a>
+for information on how to design great wearable apps.</p>
+
+<h2 id="CustomNotifications">Create Custom Notifications</h2>
+
+<p>
+In general, you should create notifications on the handheld and let them
+automatically sync to the wearable. This lets you build your notifications
+once and have them appear on many types of devices (not just wearables, but
+eventually Auto and TV) without having to design them for different
+form factors.</p>
+
+<p>If the standard notification styles don't work for you (such as
+{@link android.support.v4.app.NotificationCompat.BigTextStyle} or
+{@link android.support.v4.app.NotificationCompat.InboxStyle}), you can display an activity with
+a custom layout. You can only create and issue custom notifications on the wearable, and the
+system does not sync these notifications to the handheld.</p>
+
+<p clas="note"><b>Note:</b> When creating custom notifications on the wearable, you can use the
+standard notification APIs (API Level 20) instead of the Support Library.
+</p>
+
+<p>To create a custom notification:</p>
+<ol>
+  <li>Create a layout and set it as the content view for the activity
+  that you want to display.
+<pre>
+public void onCreate(Bundle bundle){
+    ...
+    setContentView(R.layout.notification_activity);
+}
+</pre>
+  </li>
+  <li>Set the activity's <code>allowEmbedded</code> property to <code>true</code>
+  in the wearable app's Android manifest. This allows an activity that you create in your wearable
+  app to be displayed by the wearable's context stream process.</li>
+<pre>
+&lt;activity android:allowEmbedded="true" ... &gt;
+...
+&lt;/activity&gt;
+</pre>
+  </li>
+  <li>Create a {@link android.app.PendingIntent} for the activity that you want to display.
+  For example:
+<pre>
+Intent notificationIntent = new Intent(this, NotificationActivity.class);
+PendingIntent notificationPendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
+        PendingIntent.FLAG_UPDATE_CURRENT);
+</pre>
+  </li>
+  <li>Build a {@link android.app.Notification} and call
+  {@link android.app.Notification.WearableExtender#setDisplayIntent setDisplayIntent()}
+  providing the {@link android.app.PendingIntent}. The system uses this
+  {@link android.app.PendingIntent} to launch the activity when
+  users view your notification.
+  </li>
+  <li>Issue the notification using the
+  {@link android.app.NotificationManager#notify notify()} method.
+  <p class="note"><b>Note:</b> The system initially displays your notification with
+  a standard template. This template works well on all watchfaces. When users swipe the notification
+  up to view it, they'll then see your activity for the notification.</p>
+  </li>
+</ol>
+<h2 id="UiLibrary">Create Layouts with the Wearable UI Library</h2>
+<p>
+There's an unofficial UI library that is automatically included when you create your wearable
+app with the Android Studio Project Wizard. You can also add the library to your <code>build.gradle</code>
+file with the following dependency declaration:
+
+<pre>
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    <b>compile 'com.google.android.support:wearable:+'</b>
+    compile 'com.google.android.gms:play-services-wearable:+'
+}
+</pre>
+This library helps you build UIs that are designed for wearables. Here are some of the major classes:
+</p>
+
+<ul>
+    <li><code>BoxInsetLayout</code> - A FrameLayout that's aware of screen shape and can box its
+    children in the center square of a round screen.</li>
+    <li><code>CardFragment</code> - A fragment that presents content within an expandable,
+    vertically scrollable card.</li>
+    <li><code>CircledImageView</code> - An image view surrounded by a circle.</li>
+    <li><code>ConfirmationActivity</code> - An activity that displays confirmation animations after the user
+    completes an action.</li>
+    <li><code>DismissOverlayView</code> - A view for implementing long-press-to-dismiss.</li>
+    <li><code>GridViewPager</code> - A layout manager that allows the user to both vertically and
+    horizontally through pages of data. You supply an implementation of a GridPagerAdapter to
+    generate the pages that the view shows.</li>
+    <li><code>GridPagerAdapter</code> - An adapter that supplies pages to a GridViewPager.</li>
+    <li><code>FragmentGridPagerAdapter</code> - An implementation of GridPagerAdapter that
+    represents each page as a fragment.</li>
+    </li>
+    <li><code>WatchViewStub</code> - A class that can inflate a specific layout,
+    depending on the shape of the device's screen.</li>
+    <li><code>WearableListView</code> - An alternative version of ListView that is optimized for
+    ease of use on small screen wearable devices. It displays a vertically scrollable list of items,
+    and automatically snaps to the nearest item when the user stops scrolling.
+    </li>
+</ul>
+
+<p class="note"><a href="{@docRoot}shareables/training/wearable-support-docs.zip">Download the full API
+reference documentation</a> for the classes above. The documentation goes over how to use
+each UI widget.</p>
diff --git a/docs/html/training/wearables/apps/packaging.jd b/docs/html/training/wearables/apps/packaging.jd
new file mode 100644
index 0000000..ea29c9d
--- /dev/null
+++ b/docs/html/training/wearables/apps/packaging.jd
@@ -0,0 +1,156 @@
+page.title=Packaging Wearable Apps
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Studio">Package with Android Studio</a></li>
+  <li><a href="#PackageManually">Package Manually</a></li>
+  <li><a href="#AssetCompression">Turn off Asset Compression</a></li>
+</ol>
+</div>
+</div>
+
+<p>When publishing to users, you must package a wearable app inside of a handheld app, 
+because users cannot browse and install apps directly on the wearable. If packaged properly,
+when users download the handheld app, the system automatically pushes the wearable app to the
+paired wearable.
+</p>
+
+<p class="note"><b>Note:</b> This feature doesn't work when you are signing your apps with
+a debug key when developing. While developing, installing apps with <code>adb install</code> or
+Android Studio directly to the wearable is required.</p>
+
+
+<h2 id="Studio">Package with Android Studio</h2>
+<p>To properly package a wearable app in Android Studio:</p>
+
+<ol>
+  <li>Declare a Gradle dependency in the handheld app's <code>build.gradle</code> file
+  that points to the wearable app module:
+<pre>
+dependencies {
+   compile 'com.google.android.gms:play-services:5.0.+@aar'
+   compile files('../../prebuilt-libs/android-support-v4.jar')
+   <b>wearApp project(':wearable')</b>
+}
+</pre>
+  </li>
+  <li>Click <b>Build > Generate Signed APK...</b> and follow the on-screen instructions
+  to specify your release keystore and sign your app. Android Studio exports the signed
+  handheld app with the wearable app embedded in it automatically into your project's root folder.
+
+  <p>Alternatively, you can create a <code>signingConfig</code> rule in the wearable and handheld
+  modules' <code>build.gradle</code> file to sign them with your release key. Both apps must be
+  signed to have the automatic pushing of the wearable app work.
+
+<pre>
+android {
+  ...
+  signingConfigs {
+    release {
+      keyAlias 'myAlias'
+      keyPassword 'myPw'
+      storeFile file('path/to/release.keystore')
+      storePassword 'myPw'
+    }
+  }
+  buildTypes {
+    release {
+      ...
+      signingConfig signingConfigs.release
+    }d
+  }
+  ...
+}
+</pre>
+  <p>Build the handheld app by clicking the Gradle button on the right vertical toolbar of
+  Android Studio and running the <b>assembleRelease</b> task. The task is located under
+  <b>Project name > Handheld module name > assembleRelease</b>.
+  </p>
+
+<p class="note"><b>Note:</b>This example embeds the password in your Gradle file, which might be undesirable. See
+<a href="{@docRoot}sdk/installing/studio-build.html#configureSigning">Configure signing settings</a>
+for information about how to create an environment variable for the passwords instead.
+</p>
+</ol>
+
+
+<h3>Signing the wearable and handheld app separately</h3>
+<p>If your build process requires signing the wearable app separately from the handheld app,
+you can declare the following Gradle rule in the handheld module's <code>build.gradle</code> to
+embed the previously-signed wearable app:</p>
+
+<pre>
+dependencies {
+  ...
+  wearApp files('/path/to/wearable_app.apk')
+}
+</pre>
+
+<p>You then sign your handheld app in any manner you wish (either with the Android Studio
+<b>Build > Generate Signed APK...</b> menu item or with Gradle <code>signingConfig</code> rules as
+described in the previous section.</p>
+
+<h2 id="PackageManually">Package Manually</h2>
+<p>
+It's still possible to package the wearable app into the handheld app manually
+if you are using another IDE or another method of building.
+</p>
+
+<ol>
+  <li>Copy the signed wearable app into your handheld project's <code>assets/</code> directory,
+  referred to as <code>path/to/wearable_app.apk</code>.</li>
+  <li>Create a <code>res/xml/wearable_app_desc.xml</code> file that contains the version and
+  path information of the wearable app:
+<pre>
+&lt;wearableApp package="com.google.android.wearable.myapp"&gt;
+  &lt;versionCode>1&lt;/versionCode&gt;
+  &lt;versionName>1.0&lt;/versionName&gt;
+  &lt;path>path/to/wearable_app.apk&lt;/path&gt;
+&lt;/wearableApp&gt;
+</pre>
+<p>
+The <code>package</code>, <code>versionCode</code>, and <code>versionName</code> are the
+same as values specified in the wearable app's <code>AndroidManifest.xml</code> file. 
+The path is the full path of <code>wearable_app.apk</code>, relative to the <code>assets/</code>
+directory.
+</p>
+</li>
+<li>
+Add a <code>meta-data</code> tag to your handheld app's <code>&lt;application&gt;</code> tag to
+reference the <code>wearable_app_desc.xml</code> file.
+<pre>
+  &lt;meta-data android:name="com.google.android.wearable.myapp" 
+                 android:resource="&#64;xml/wearable_app_desc"/&gt;
+</pre>
+</li>
+<li>Build and sign the handheld app.</li>
+</ol>
+
+<h2 id="AssetCompression">Turn off Asset Compression</h2>
+<p>
+Many build tools automatically compress any files added to the <code>assets/</code>
+directory of an Android app. Because the wearable APK is already zipped, these tools re-compress the
+wearable APK and the wearable app installer can no longer read the wearable app.
+</p>
+
+<p> When this happens, the installation fails. On the handheld app, the <code>PackageUpdateService</code>
+logs the following error: "this file cannot be opened as a file descriptor; it is probably compressed."
+
+<p> To prevent this error in Android Studio, update your handheld app's <code>build.gradle</code> file
+with the following declaration:
+</p>
+
+<pre>
+android {
+  aaptOptions {
+    noCompress "apk"
+  }
+}
+</pre>
+
+<p>If you are using another build process, ensure that you don't doubly compress the wearable app.</p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/apps/voice.jd b/docs/html/training/wearables/apps/voice.jd
new file mode 100644
index 0000000..3dea5d7
--- /dev/null
+++ b/docs/html/training/wearables/apps/voice.jd
@@ -0,0 +1,274 @@
+page.title=Adding Voice Capabilities
+@jd:body
+
+<div id="tb-wrapper">
+  <div id="tb">
+
+    <!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+    <h2>This lesson teaches you to</h2>
+    <ol>
+      <li><a href="#SystemProvided">Declare System-provided Voice Actions</a></li>
+      <li><a href="#AppProvided">Declare App-provided Voice Actions</a></li>
+      <li><a href="#FreeFormSpeech">Obtaining Free-form Speech Input</a></li>
+    </ol>
+    <h2>You should also read</h2>
+    <ul>
+      <li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
+    </ul>
+  </div>
+</div>
+
+<p>Voice actions are an important part of the wearable experience. They let users carry
+out actions hands-free and quickly. Wear provides two types of voice actions:</p>
+
+<dl>
+  <dt><b>System-provided</b></dt>
+  <dd>These voice actions are task-based and are built
+  into the Wear platform. You filter for them in the activity that you want to start when the
+  voice action is spoken. Examples include "Take a note" or "Set an alarm".</dd>
+  <dt><b>App-provided</b></dt>
+  <dd>These voice actions are app-based, and you declare them just like a launcher icon.
+  Users say "Start <Your App Name>" to use these voice actions and an activity that you specify
+  starts.</dd>
+</dl>
+
+<h2 id="SystemProvided" style="clear:right">Declare System-provided Voice Actions</h2>
+<p>
+The Android Wear platform provides several voice intents that are based on user actions such
+as "Take a note" or "Set an alarm". This allows users to say what they want to do and let
+the system figure out the best activity to start.</p>
+
+<p>When users speak the voice action, your app can filter for the intent that is fired to start
+an activity. If you want to start a service to do something in the background, show an activity as
+a visual cue and start the service in the activity. Make sure to call
+{@link android.app.Activity#finish finish()} when you want to get rid of the visual cue.
+</p>
+
+<p>For example, for the "Take a note" command, declare this intent filter to start an activity
+named <code>MyNoteActivity</code>:
+</p>
+
+<pre>
+  &lt;activity android:name="MyNoteActivity"&gt;
+      &lt;intent-filter&gt;
+          &lt;action android:name="android.intent.action.SEND" /&gt;
+          &lt;category android:name="com.google.android.voicesearch.SELF_NOTE" /&gt;
+      &lt;/intent-filter&gt;
+  &lt;/activity&gt;
+</pre>
+
+<p>Here is a list of the voice intents supported by the Wear platform:</p>
+
+<table>
+  <tr>
+    <th>Name</th>
+    <th>Example Phrases</th>
+    <th>Intent</th>
+  </tr>
+
+  <tr>
+    <td>Call a car/taxi</td>
+    <td>"OK Google, get me a taxi"<br/><br/>"OK Google, call me a car"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd>
+          <code>com.google.android.gms.actions.RESERVE_TAXI_RESERVATION</code>
+        </dd>
+      </dl>
+    </td>
+  </tr>
+
+  <tr>
+    <td>Take a note</td>
+    <td>"OK Google, take a note"<br/><br/>"OK Google, note to self"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>android.intent.action.SEND</code></dd>
+        <dt>Category</dt>
+        <dd><code>com.google.android.voicesearch.SELF_NOTE</code></dd>
+        <dt>Extras</dt>
+        <dd><code>android.content.Intent.EXTRA_TEXT</code> - a string with note body</dd>
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
+    <td>Set alarm</td>
+    <td>"OK Google, set an alarm for 8 AM"<br/><br/>"OK Google, wake me up at 6 tomorrow"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>android.intent.action.SET_ALARM</code></dd>
+        <dt>Extras</dt>
+        <dd><code>android.provider.AlarmClock.EXTRA_HOUR</code> - an integer with the hour of
+        the alarm.
+        <p><code>android.provider.AlarmClock.EXTRA_MINUTES</code> -
+        an integer with the minute of the alarm
+        <p>(these 2 extras are optional, either none or
+        both are provided)</p></dd>
+
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
+    <td>Set timer</td>
+    <td>"Ok Google, set a timer for 10 minutes"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>android.provider.AlarmClock.ACTION_SET_TIMER</code></dd>
+        <dt>Extras</dt>
+        <dd><code>android.provider.AlarmClock.EXTRA_LENGTH</code> - an integer in the range of
+        1 to 86400 (number of seconds in 24 hours) representing the length of the timer </dd>
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
+    <td>Start/Stop a bike ride</td>
+    <td>"OK Google, start cycling"<br/><br/>"OK Google, start my bike ride"<br/><br/>"OK Google, stop cycling"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>vnd.google.fitness.TRACK</code></dd>
+        <dt>Mime Type</dt>
+        <dd><code>vnd.google.fitness.activity/biking</code></dd>
+        <dt>Extras</dt>
+        <dd><code>actionStatus</code> - a string with the value <code>ActiveActionStatus</code>
+        when starting and <code>CompletedActionStatus</code> when stopping.</dd>
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
+    <td>Start/Stop a run</td>
+    <td>"OK Google, track my run"<br/><br/>"OK Google, start running"<br/><br/>"OK Google, stop running"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>vnd.google.fitness.TRACK</code></dd>
+        <dt>MimeType</dt>
+        <dd><code>vnd.google.fitness.activity/running</code></dd>
+        <dt>Extras</dt>
+        <dd><code>actionStatus</code> - a string with the value <code>ActiveActionStatus</code>
+        when starting and <code>CompletedActionStatus</code> when stopping</dd>
+      </dl>
+   </td>
+  </tr>
+
+
+  <tr>
+    <td>Start/Stop a workout</td>
+    <td>"OK Google, start a workout"<br/><br/>"OK Google, track my workout"<br/><br/>"OK Google, stop workout"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>vnd.google.fitness.TRACK</code></dd>
+        <dt>MimeType</dt>
+        <dd><code>vnd.google.fitness.activity/other</code></dd>
+        <dt>Extras</dt>
+        <dd><code>actionStatus</code> - a string with the value <code>ActiveActionStatus</code>
+        when starting and <code>CompletedActionStatus</code> when stopping</dd>
+        </dd>
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
+    <td>Show heart rate</td>
+    <td>"OK Google, what’s my heart rate?"<br/><br/>"OK Google, what’s my bpm?"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>vnd.google.fitness.VIEW</code></dd>
+        <dt>Mime Type</dt>
+        <dd><code>vnd.google.fitness.data_type/com.google.heart_rate.bpm</code></dd>
+        </dd>
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
+    <td>Show step count</td>
+    <td>"OK Google, how many steps have I taken?"<br/><br/>"OK Google, what’s my step count?"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>vnd.google.fitness.VIEW</code></dd>
+        <dt>Mime Type</dt>
+        <dd><code>vnd.google.fitness.data_type/com.google.step_count.cumulative</code></dd>
+        </dd>
+      </dl>
+   </td>
+  </tr>
+
+</table>
+
+<p>
+For documentation on registering for platform intents and accessing the extras information
+contained in them, see <a href="{@docRoot}guide/components/intents-common.html">Common intents</a>.
+</p>
+
+<h2 id="AppProvided">Declare App-provided Voice Actions</h2>
+<p>
+If none of the platform voice intents work for you, you can start your apps directly with
+a "Start MyActivityName" voice action. </p>
+
+<p>Registering for a "Start" action is the same as registering
+for a launcher icon on a handheld. Instead of requesting an app icon in a launcher,
+your app requests a voice action instead.</p>
+
+<p>To specify the text to say after "Start", specify a <code>label</code> attribute for the activtiy
+that you want to start. For example, this intent filter recognizes the
+"Start MyRunningApp" voice action and launches <code>StartRunActivity</code>.
+</p>
+
+<pre>
+&lt;application&gt;
+  &lt;activity android:name="StartRunActivity" android:label="MyRunningApp"&gt;
+      &lt;intent-filter&gt;
+          &lt;action android:name="android.intent.action.MAIN" /&gt;
+          &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
+      &lt;/intent-filter&gt;
+  &lt;/activity&gt;
+&lt;/application&gt;
+</pre>
+
+<h2 id="FreeFormSpeech">Obtaining Free-form Speech Input</h2>
+<p>In addition to using voice actions to launch activities, you can also call the system's
+built-in Speech Recognizer activity to obtain speech input from users. This is useful to obtain input
+from users and then process it, such as doing a search or sending it as a message.</p>
+
+In your app, you call {@link android.app.Activity#startActivityForResult startActivityForResult()} using
+the {@link android.speech.RecognizerIntent#ACTION_RECOGNIZE_SPEECH} action. This starts the
+ and then handle the result
+in {@link android.app.Activity#onActivityResult onActivityResult()}.
+<pre>
+private static final int SPEECH_REQUEST_CODE = 0;
+
+// Create an intent that can start the Speech Recognizer activity
+private void displaySpeechRecognizer() {
+    Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+    intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
+// Start the activity, the intent will be populated with the speech text
+    startActivityForResult(intent, SPEECH_REQUEST_CODE);
+}
+
+// This callback is invoked when the Speech Recognizer returns.
+// This is where you process the intent and extract the speech text from the intent.
+&#64;Override
+protected void onActivityResult(int requestCode, int resultCode,
+        Intent data) {
+    if (requestCode == SPEECH_REQUEST && resultCode == RESULT_OK) {
+        List&lt;String&gt; results = data.getStringArrayListExtra(
+                RecognizerIntent.EXTRA_RESULTS);
+        String spokenText = results.get(0);
+        // Do something with spokenText
+    }
+    super.onActivityResult(requestCode, resultCode, data);
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/accessing.jd b/docs/html/training/wearables/data-layer/accessing.jd
new file mode 100644
index 0000000..4babd0a
--- /dev/null
+++ b/docs/html/training/wearables/data-layer/accessing.jd
@@ -0,0 +1,59 @@
+page.title=Accessing the Wearable Data Layer
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li>Set up a Google Play services client to use the Wearable Data Layer APIs</li>
+</ol>
+
+<h2>Dependencies and Prerequisites</h2>
+<ol>
+  <li><a href="{@docRoot}training/wearables/apps/environment.html">Creating
+  Wearable Apps > Setting up Your Environment</a></li>
+  <li><a href="{@docRoot}training/wearables/apps/creating.html">Creating
+    Wearable Apps > Creating a Project</a></li>
+</ol>
+</div>
+</div>
+
+<p>To call the data layer API, create an instance of
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>,
+the main entry point for any of the Google Play services APIs.
+</p>
+
+<p>
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>
+provides a builder that makes it easy to create an instance of the client.
+A minimal <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a> looks like this:
+</p>
+
+<p class="note"><b>Note:</b> For now, this minimal client is enough to get started. However, see
+<a href="{@docRoot}google/auth/api-client.html">Accessing Google Play services APIs</a>
+for more information about creating a<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>,
+implementing its callbacks, and handling error cases.</p>
+
+<pre style="clear:right">
+GoogleApiClient mGoogleAppiClient = new GoogleApiClient.Builder(this)
+        .addConnectionCallbacks(new ConnectionCallbacks() {
+                &#64;Override
+                public void onConnected(Bundle connectionHint) {
+                    Log.d(TAG, "onConnected: " + connectionHint);
+                }
+                &#64;Override
+                public void onConnectionSuspended(int cause) {
+                    Log.d(TAG, "onConnectionSuspended: " + cause);
+                }
+        })
+        .addOnConnectionFailedListener(new OnConnectionFailedListener() {
+                &#64;Override
+                public void onConnectionFailed(ConnectionResult result) {
+                    Log.d(TAG, "onConnectionFailed: " + result);
+                }
+            })
+        .addApi(Wearable.API)
+        .build();
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/assets.jd b/docs/html/training/wearables/data-layer/assets.jd
new file mode 100644
index 0000000..73ebb73
--- /dev/null
+++ b/docs/html/training/wearables/data-layer/assets.jd
@@ -0,0 +1,123 @@
+page.title=Transferring Assets
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#TransferAsset">Transfer an Asset</a></li>
+  <li><a href="#ReceiveAsset">Receive an Asset</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>
+To send large blobs of binary data over the Bluetooth transport, such as images, attach an
+<a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html">Asset</a> to a
+data item and the put the data item into the replicated data store.
+</p>
+
+<p>Assets automatically handle caching of data to prevent retransmission and conserve Bluetooth bandwidth.
+A common pattern is for a handheld app to download an image, shrink it to an appropriate size
+for display on the wearable, and transmit it to the wearable app as an Asset. The following examples
+demonstrates this pattern.
+</p>
+
+<p class="note"><b>Note:</b> Although the size of data items are limited to 100KB,
+assets can be as large as desired. However, transferring large assets affect the
+user experience in many cases, so test your apps to ensure that they perform well
+if you're transferring large assets.
+<p>
+
+<h2 id="TransferAsset">Transfer an Asset</h2>
+<p>Create the asset using one of the <code>create...()</code> methods in the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html"><code>Asset</code></a> class.
+Here, we convert a bitmap to a byte stream and then call
+<a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html#createFromBytes(byte[])"><code>createFromBytes()</code></a>
+to create the asset.
+</p>
+
+<pre>
+private static Asset createAssetFromBitmap(Bitmap bitmap) {
+    final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+    bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
+    return Asset.createFromBytes(byteStream.toByteArray());
+}
+</pre>
+
+<p>When you have an asset, attach it to a data item with the <code>putAsset()</code> method in
+
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html"><code>DataMap</code></a>
+or
+<a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html"><code>PutDataRequest</code></a>
+and then put the data item into the data store with
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>:
+</p>
+
+<p><b>Using PutDataRequest</b></p>
+<pre>
+Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
+Asset asset = createAssetFromBitmap(bitmap);
+PutDataRequest request = PutDataRequest.create("/image");
+request.putAsset("profileImage", asset);
+Wearable.DataApi.putDataItem(mGoogleApiClient, request);
+</pre>
+
+<p><b>Using PutDataMapRequest</b></p>
+<pre>
+Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
+Asset asset = createAssetFromBitmap(bitmap);
+PutDataMapRequest dataMap = PutDataMapRequest.create("/image");
+dataMap.getDataMap().putAsset("profileImage", asset)
+PutDataRequest request = dataMap.asPutDataRequest();
+PendingResult&lt;DataApi.DataItemResult&gt; pendingResult = Wearable.DataApi
+        .putDataItem(mGoogleApiClient, request);
+</pre>
+
+<h2 id="ReceiveAsset">Receive assets</h2>
+
+<p>
+When an asset is created, you probably want to read and extract
+it on other side of the connection. Here's an example of how to implement the
+callback to detect an asset change and extract the Asset:
+</p>
+
+<pre>
+&#64;Override
+public void onDataChanged(DataEventBuffer dataEvents) {
+  for (DataEvent event : dataEvents) {
+    if (event.getType() == DataEvent.TYPE_CHANGED &&
+        event.getDataItem().getUri().getPath().equals("/image")) {
+      BundleDataItem bundleDataItem = BundleDataItem.fromDataItem(dataItem); 
+      Asset profileAsset = bundleDataItem.getData().getParcelable("profileImage");
+      Bitmap bitmap = loadBitmapFromAsset(profileAsset);
+      // Do something with the bitmap
+    }
+  }
+}
+
+public Bitmap loadBitmapFromAsset(Asset asset) {
+    if (asset == null) {
+        throw new IllegalArgumentException("Asset must be non-null");
+    }
+    ConnectionResult result =
+           mGoogleApiClient.blockingConnect(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    if (!result.isSuccess()) {
+        return null;
+    }
+    // convert asset into a file descriptor and block until it's ready
+    InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
+            mGoogleApiClient, asset).await().getInputStream();
+            mGoogleApiClient.disconnect();
+
+    if (assetInputStream == null) {
+        Log.w(TAG, "Requested an unknown Asset.");
+        return null;
+    }
+    // decode the stream into a bitmap
+    return BitmapFactory.decodeStream(assetInputStream);
+}
+</pre>
diff --git a/docs/html/training/wearables/data-layer/data-items.jd b/docs/html/training/wearables/data-layer/data-items.jd
new file mode 100644
index 0000000..c39e37a
--- /dev/null
+++ b/docs/html/training/wearables/data-layer/data-items.jd
@@ -0,0 +1,124 @@
+page.title=Syncing Data Items
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#SyncData">Sync Data with a Data Map</a></li>
+  <li><a href="#ListenEvents">Listen for Data Item Events</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>
+A <a href="@{docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
+defines the data interface that the system uses to synchronize data between handhelds
+and wearables. A <a href="@{docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a> generally
+consists of the following items:</p>
+<ul>
+  <li><b>Payload</b> - A byte array, which you can set with whatever data you wish, allowing you
+  to do your own object serialization and deserialization. The size of the payload is limited
+  to 100KB.</li>
+  <li><b>Path</b> - A unique string that must start with a forward slash (for instance,
+  <code>"/path/to/data"</code>)</li>
+</ul>
+
+<p>
+You normally don't implement <a href="@{docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
+directly. Instead, you:
+
+<ol>
+  <li>Create a <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html"><code>PutDataRequest</code></a> object,
+  specifying a string path to uniquely identify the item.
+  </li>
+  <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">setData()</a> to set
+  the payload.
+  </li>
+  <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>DataApi.putDataItem()</code></a> to request the system to create the data item.
+  </li>
+  <li>When requesting data items, the system returns objects
+  that properly implement the <a href="{@docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a> interface.
+  </li>
+</ol>
+
+<p>
+However, instead of working with raw bytes using <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">setData()</a>,
+we recommend you <a href="#data-map">use a data map</a>, which exposes
+a data item in an easy-to-use {@link android.os.Bundle}-like interface.
+</p>
+
+<h2 id="SyncData">Sync Data with a Data Map</h2>
+<p>
+When possible, use the <a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html"><code>DataMap</code></a> class,
+which lets you work with data items in the form of an Android {@link android.os.Bundle},
+so object serialization and de-serialization is done for you, and you can manipulate data with key-value pairs.
+</p>
+
+<p>To use a data map:</p>
+
+<ol>
+  <li>Create a
+<a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataMapRequest.html"><code>PutDataMapRequest</code></a>
+object, setting the path of the data item.
+<p class="note"><b>Note:</b> The path string is a unique identifier for the
+data item that allows you to access it from either side of the connection. The path must begin
+with a forward slash. If you're using hierarchical data in your
+app, you should create a path scheme that matches the structure of the data. 
+</p>
+</li>
+<li>Call
+<a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataMapRequest.html#getDataMap()"><code>PutDataMapRequest.getDataMap()</code></a>
+</a> to obtain a data map that you can set values on.</li>
+  <li>Set any desired values for the data map using the <code>put...()</code> methods, such as
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html#putString(java.lang.String, java.lang.String)"><code>putString()</code></a>.
+  </li>
+  <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataMapRequest.html#asPutDataRequest()"><code>PutDataMapRequest.asPutDataRequest()</code></a>
+  to obtain a <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html"><code>PutDataRequest</code></a> object.
+   </li>
+  <li>Call <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>DataApi.putDataItem()</code></a> to request the system to create the data item.
+  <p class="note"><b>Note:</b>
+  If the handset and wearable devices are disconnected,
+  the data is buffered and and synced when the connection is re-established.
+  </p>
+  </li>
+</ol>
+
+<p>The following example shows how to create a data map, set data on it, and create it:</p>
+
+<pre>
+PutDataMapRequest dataMap = PutDataMapRequest.create("/count");
+dataMap.getDataMap().putInt(COUNT_KEY, count++);
+PutDataRequest request = dataMap.asPutDataRequest();
+PendingResult&lt;DataApi.DataItemResult&gt; pendingResult = Wearable.DataApi
+        .putDataItem(mGoogleApiClient, request);
+</pre>
+
+<h2 id="ListenEvents">Listen for Data Item Events</h2>
+If one side of the data layer connection changes a data item, you probably want
+to be notified of any changes on the other side of the connection.
+You can do this by implementing a listener for data item events.
+
+<p>For example, here's what a typical callback looks like to carry out certain actions
+when data changes.</p>
+
+<pre>
+&#64;Override
+public void onDataChanged(DataEventBuffer dataEvents) {
+    for (DataEvent event : dataEvents) {
+        if (event.getType() == DataEvent.TYPE_DELETED) {
+            Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
+        } else if (event.getType() == DataEvent.TYPE_CHANGED) {
+             Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
+        }
+    }
+}
+</pre>
+<p>
+This is just a snippet that requires more implementation details. Learn about
+how to implement a full listener service or activity in
+<a href="{@docRoot}training/wearables/data-layer/listeners.html">Listening for Data Layer Events</a>.
+</p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd
new file mode 100644
index 0000000..0146c4e
--- /dev/null
+++ b/docs/html/training/wearables/data-layer/events.jd
@@ -0,0 +1,312 @@
+page.title=Handling Data Layer Events
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Wait">Wait for the Status of Data Layer Calls</a></li>
+  <li><a href="#Listen">Listen for Data Layer Events</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>When you make calls with the data layer, you can receive the status
+of the call when it completes as well as listen for any changes that
+the call ends up making with listeners.
+</p>
+
+<h2 id="Wait">Wait for the Status of Data Layer Calls</h2>
+
+<p>You'll notice that calls to the data layer API sometimes return a
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,
+such as
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>.
+As soon as the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> is created,
+the operation is queued in the background. If you do nothing else after this, the operation
+eventually completes silently. However, you'll usually want to do something with the result
+after the operation completes, so the
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
+lets you wait for the result status, either synchronously or asynchronously.
+</p>
+
+<h3 id="async-waiting">Asynchronously waiting</h3>
+<p>If your code is running on the main UI thread, do not making blocking calls
+to the data layer API. You can run the calls asynchronously by adding a callback
+to the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object,
+which fires when the operation is completed:</p>
+<pre>
+pendingResult.setResultCallback(new ResultCallback&lt;DataItemResult&gt;() {
+    &#64;Override
+    public void onResult(final DataItemResult result) {
+        if(result.getStatus().isSuccess()) {
+        Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
+    }
+});
+</pre>
+
+<h3 id="sync-waiting">Synchronously waiting</h3>
+<p>If your code is running on a separate handler thread in a background service (which is the case
+in a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>),
+it's fine for the calls to block. In this case, you can call
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a>
+on the PendingResult object, which will block until the request has completed, and return a Result
+object:
+</p>
+
+<pre>
+DataItemResult result = pendingResult.await();
+if(result.getStatus().isSuccess()) {
+    Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
+}
+</pre>
+
+
+<h2 id="Listen">Listen for Data Layer Events </h2>
+<p>Because the data layer synchronizes and sends data across the handheld and
+wearable, you normally want to listen for important events, such as when data items
+are created, messages are received, or when the wearable and handset are connected.
+</p>
+<p>To listen for data layer events, you have two options:</p>
+
+<ul>
+  <li>Create a service that extends
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
+  </li>
+  <li>Create an activity that implements
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>.
+  </li>
+</ul>
+
+<p>With both these options, you override any of the data event callbacks that you care about
+handling in your implementation.</p>
+
+<h3 id="listener-service">With a WearableListenerService</h3>
+
+<p>
+You typically create instances of this service in both your wearable and handheld apps. If you
+don't care about data events in one of these apps, then you don't need to implement this
+service in that particular app.</p>
+
+<p>For example, you can have a handheld app that sets and gets data item objects and a wearable app
+that listens for these updates to update it's UI. The wearable never updates any of the data items,
+so the handheld app doesn't listen for any data events from the wearable app.</p>
+
+<p>You can listen for the following events with
+<a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:</p>
+
+<ul>
+  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>
+- Called when data item objects are created, changed, or deleted. An event on one side of a connection
+triggers an this callback on both sides.</li>
+  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onMessageReceived()</code></a>
+-  A message sent from one side of a connection triggers this callback on the other side of the connection.</li>
+  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onPeerConnected()</code></a>
+  and <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a> -
+  Called when connection with the handheld or wearable is connected or disconnected. 
+  Changes in connection state on one side of the connection triggers these callbacks on both sides of the connection.
+  </li>
+</ul>
+
+<p>To create a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:</p>
+
+<ol>
+  <li>Create a class that extends
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
+  </li>
+  <li>Listen for the events that you care about, such as
+  <a href="{@docRoot}/reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>.
+  </li>
+  <li>Declare an intent filter in your Android manifest to notify the system about your
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
+  This allows the system to bind your service as needed.
+  </li>
+</ol>
+
+  <p>The following example shows how to implement a simple
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:
+  </p>
+
+<pre>
+public class DataLayerListenerService extends WearableListenerService {
+
+    private static final String TAG = "DataLayerSample";
+    private static final String START_ACTIVITY_PATH = "/start-activity";
+    private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
+
+    &#64;Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onDataChanged: " + dataEvents);
+        }
+        final List<DataEvent> events = FreezableUtils
+                .freezeIterable(dataEvents);
+
+        GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .build();
+
+        ConnectionResult connectionResult =
+                googleApiClient.blockingConnect(30, TimeUnit.SECONDS);
+
+        if (!connectionResult.isSuccess()) {
+            Log.e(TAG, "Failed to connect to GoogleApiClient.");
+            return;
+        }
+
+        // Loop through the events and send a message
+        / to the node that created the data item.
+        for (DataEvent event : events) {
+            Uri uri = event.getDataItem().getUri();
+
+            // Get the node id from the host value of the URI
+            String nodeId = uri.getHost();
+            // Set the data of the message to be the bytes of the URI.
+            byte[] payload = uri.toString().getBytes();
+
+            // Send the RPC
+            Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
+                    DATA_ITEM_RECEIVED_PATH, payload);
+        }
+    }
+}
+</pre>  
+
+<p>Here's the corresponding intent filter in the Android manifest file:</p>
+
+<pre>
+&lt;service android:name=".DataLayerListenerService"&gt;
+  &lt;intent-filter&gt;
+      &lt;action android:name="com.google.android.gms.wearable.BIND_LISTENER" /&gt;
+  &lt;/intent-filter&gt;
+&lt;/service&gt;
+</pre>
+
+
+<h4>Permissions within Data Layer Callbacks</h4>
+
+<p>In order to deliver callbacks to your application for data layer events, Google Play services
+binds to your <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>,
+and calls your callbacks via IPC. This has the consequence
+that your callbacks inherit the permissions of the calling process.</p>
+
+<p>If you try to perform a privileged operation within a callback, the security check fails because your callback is
+running with the identity of the calling process, instead of the identity of your app's
+process.</p>
+
+<p>To fix this, call {@link android.os.Binder#clearCallingIdentity} </a>,
+to reset identity after crossing the IPC boundary, and then restore identity with
+{@link android.os.Binder#restoreCallingIdentity restoreCallingIdentity()} when
+you've completed the privileged operation:
+</p>
+
+<pre>
+long token = Binder.clearCallingIdentity();
+try {
+    performOperationRequiringPermissions();
+} finally {
+    Binder.restoreCallingIdentity(token);
+}
+</pre>
+
+<h3 id="Listen">With a Listener Activity</h3>
+
+<p>
+If your app only cares about data layer events when the user is interacting
+with the app and does not need a long-running service to handle every data
+change, you can listen for events in an activity by implementing one or more
+of the following interfaces:
+
+<ul>
+  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a></li>
+  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.MessageListener.html"><code>MessageApi.MessageListener</code></a></li>
+  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html"><code>NodeApi.NodeListener</code></a></li>
+</ul>
+</p>
+
+<p>To create an activity that listens for data events:</p>
+<ol>
+<li>Implement the desired interfaces.</li>
+<li>In {@link android.app.Activity#onCreate}, create an instance of
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>
+to work with the data layer API.
+<li>
+In {@link android.app.Activity#onStart onStart()}, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"><code>connect()</code></a> to connect the client to Google Play services.
+</li>
+<li>When the connection to Google Play services is established, the system calls
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>. This is where you call
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.addListener()</code></a>,
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.addListener()</code></a>,
+  or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.NodeApi.NodeListener)"><code>NodeApi.addListener()</code></a>
+  to notify Google Play services that your activity is interested in listening for data layer events.
+</li>
+<li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.removeListener()</code></a>,
+<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.removeListener()</code></a>,
+or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.NodeApi.NodeListener)"><code>NodeApi.removeListener()</code></a>.
+</li>
+<li>Implement <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code>,
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerConnected(com.google.android.gms.wearable.Node)"><code>onMessageReceived()</code></a>,
+    <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerConnected(com.google.android.gms.wearable.Node)"><code>onPeerConnected()</code></a>, and
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a>, depending on the interfaces that you implemented.
+</li>
+</ol>
+
+<p>Here's an example that implements
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p>
+
+<pre>
+public class MainActivity extends Activity implements
+        DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main);
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    &#64;Override
+    protected void onStart() {
+        super.onStart();
+        if (!mResolvingError) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+   &#64;Override
+    public void onConnected(Bundle connectionHint) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Connected to Google Api Service");
+        }
+        Wearable.DataApi.addListener(mGoogleApiClient, this);
+    }
+
+    &#64;Override
+    protected void onStop() {
+        if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
+            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+            mGoogleApiClient.disconnect();
+        }
+        super.onStop();
+    }
+
+    &#64;Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        for (DataEvent event : dataEvents) {
+            if (event.getType() == DataEvent.TYPE_DELETED) {
+                Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
+            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
+                 Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
+            }
+        }
+    }
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/index.jd b/docs/html/training/wearables/data-layer/index.jd
new file mode 100644
index 0000000..3e06df1
--- /dev/null
+++ b/docs/html/training/wearables/data-layer/index.jd
@@ -0,0 +1,87 @@
+page.title=Sending and Syncing Data
+@jd:body
+
+<div id="tb-wrapper">
+  <div id="tb">
+
+    <!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+    <h2>Dependencies and prerequisites</h2>
+    <ul>
+      <li>Android 4.3 (API Level 18) or higher on the handset device</li>
+      <li>The latest version of <a href="{@docRoot}google/play">Google Play services</a></li>
+      <li>An Android Wear device or Wear AVD</li>
+    </ul>
+  </div>
+</div>
+
+<p>
+The Wearable Data Layer API, which is part of Google Play services, provides a communication channel
+for your handheld and wearable apps. The API consists of a set of data objects that the system can
+send and synchronize over the wire and listeners that notify your apps of important events with
+the data layer:</p>
+
+<dl>
+  <dt><b>Data Items</b></dt>
+  <dd>A <a href="{@docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
+  provides data storage with automatic syncing between the handheld and
+  wearable.</dd>
+
+  <dt><b>Messages</b></dt>
+  <dd>The <a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html"><code>MessageApi</code></a> class
+  can send messages designed for "fire-and-forget" commands, such as controlling a handheld's
+  media player from the wearable or starting an intent on the wearable from the handheld.
+  The system always delivers the message when the handheld and wearable are connected and delivers
+  an error when the devices are disconnected. Messages are great for one-way requests or for a
+  request/response communication model.</dd>
+
+  <dt><b>Asset</b></dt>
+  <dd><a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html"><code>Asset</code></a> objects are for
+  sending binary blobs of data, such as images. You attach assets to data items and the system
+  automatically takes care of the transfer for you, conserving Bluetooth bandwidth by caching large assets
+  to avoid re-transmission.</dd>
+
+  <dt><b>WearableListenerService</b> (for services)</dt>
+  <dd><p>Extending <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>
+  lets you listen for important data layer events in a service. The system manages the lifecycle of
+  the <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>,
+  binding to the service when it needs to send data items or messages and unbinding the service when no work is needed.</p>
+  </dd>
+
+  <dt><b>DataListener</b> (for foreground activities)</dt>
+  <dd>
+  Implementing <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataListener</code></a>
+  in an activity lets you listen for important data layer events when an activity
+  is in the foreground. Using this instead of the
+  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>
+  lets you listen for changes only when the user is actively using your app.
+  </dd>
+</dl>
+
+<p class="warning"><b>Warning:</b>
+Because these APIs are designed for communication between handhelds and wearables,
+these are the only APIs you should use to set up communication between these
+devices. For instance, don't try to open low-level sockets to create a communication
+channel.
+</p>
+
+<h2>Lessons</h2>
+  <dl>
+    <dt><a href="{@docRoot}training/wearables/data-layer/data-items.html">Accessing the Wearable Data Layer</a></dt>
+    <dd>This lesson shows you how to create a client to access the Data Layer APIs.</dd>
+
+    <dt><a href="{@docRoot}training/wearables/data-layer/data-items.html">Syncing Data Items</a></dt>
+    <dd>Data items are objects that are stored in a replicated data store that is automatically
+      synced from handhelds to wearables.</dd>
+
+    <dt><a href="{@docRoot}training/wearables/data-layer/assets.html">Transferring Assets</a></dt>
+      <dd>Assets are binary blobs of data that you typically use to transfer images or media.</dd>
+
+    <dt><a href="{@docRoot}training/wearables/data-layer/stacks.html">Sending and Receiving Messages</a></dt>
+      <dd>Messages are designed for fire-and-forget messages that you can send back and forth
+      between the wearable and handheld.</dd>
+
+    <dt><a href="{@docRoot}training/wearables/data-layer/events.html">Handling Data Layer Events</a></dt>
+      <dd>Be notified of changes and events to the data layer.</dd>
+  </dl>
+
+</div>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/messages.jd b/docs/html/training/wearables/data-layer/messages.jd
new file mode 100644
index 0000000..a9dec75
--- /dev/null
+++ b/docs/html/training/wearables/data-layer/messages.jd
@@ -0,0 +1,99 @@
+page.title=Sending and Receiving Messages
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#SendMessage">Send a Message</a></li>
+  <li><a href="#ReceiveMessage">Receive a Message</a></li>
+</ol>
+</div>
+</div>
+
+<p>You send messages using the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html"><code>MessageApi</code></a>
+and attach the following items to the message:</p>
+
+<ul>
+  <li>An arbitrary payload (optional)</li>
+  <li>A path that uniquely identifies the message's action</li>
+</ul>
+<p>
+Unlike data items, there is no syncing between the handheld and wearable apps.
+Messages are a one-way communication mechanism that's meant for
+"fire-and-forget" tasks, such as sending a message to the wearable
+to start an activity. You can also use messages in request/response model
+where one side of the connection sends a message, does some work,
+sends back a response message.</p>
+
+<h2 id="SendMessage">Send a Message</h2>
+
+<p>The following example shows how to send a message that indicates to the other
+side of the connect to start an activity.
+This call is made synchronously, which blocks until the message
+is received or when the request times out:
+</p>
+
+<p class="note"><b>Note:</b> Read more about asynchronous and synchronous calls
+to Google Play services and when to use each in
+<a href="google/auth/api-client.html#Communicating">Communicate with Google Play Services</a>.
+</p>
+
+<pre>
+Node node; // the connected device to send the message to
+GoogleApiClient mGoogleApiClient;
+public static final START_ACTIVITY_PATH = "/start/MainActivity";
+...
+
+    SendMessageResult result = Wearable.MessageApi.sendMessage(
+            mGoogleApiClient, node, START_ACTIVITY_PATH, null).await();
+    if (!result.getStatus().isSuccess()) {
+        Log.e(TAG, "ERROR: failed to send Message: " + result.getStatus());
+    }
+</pre>
+
+<p>
+Here's a simple way to get a list of connected nodes that you can potentially
+send messages to:</p>
+
+<pre>
+private Collection&lt;String&gt; getNodes() {
+    HashSet &lt;String&gt;results= new HashSet&lt;String&gt;();
+    NodeApi.GetConnectedNodesResult nodes =
+            Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
+    for (Node node : nodes.getNodes()) {
+        results.add(node.getId());
+    }
+    return results;
+}
+</pre>
+
+<h2 id="ReceiveMessage">Receiving a Message</h2>
+
+<p>
+
+To be notified of received messages, you implement a listener for message events.
+This example shows how you might do this by checking the <code>START_ACTIVITY_PATH</code>
+that the previous example used to send the message. If this condition is <code>true</code>,
+a specific activity is started.
+</p>
+
+<pre>
+&#64;Override
+public void onMessageReceived(MessageEvent messageEvent) {
+    if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
+        Intent startIntent = new Intent(this, MainActivity.class);
+        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(startIntent);
+    }
+}
+</pre>
+
+<p>
+This is just a snippet that requires more implementation details. Learn about
+how to implement a full listener service or activity in
+<a href="#listening">Listening for Data Layer Events</a>.
+</p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/creating.jd b/docs/html/training/wearables/notifications/creating.jd
new file mode 100644
index 0000000..a61fa07
--- /dev/null
+++ b/docs/html/training/wearables/notifications/creating.jd
@@ -0,0 +1,295 @@
+page.title=Creating a Notification
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Import">Import the Necessary Classes</a></li>
+  <li><a href="#NotificationBuilder">Create Notifications with the Notification Builder</a></li>
+  <li><a href="#ActionButtons">Add Action Buttons</a></li>
+  <li><a href="#SpecifyWearableOnlyActions">Specify Wearable-only Actions</a></li>
+  <li><a href="#BigView">Add a Big View</a></li>
+  <li><a href="#AddWearableFeatures">Add Wearable Features for a Notification</a></li>
+  <li><a href="#Deliver">Deliver the Notification</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>To build handheld notifications that are also sent to wearables, use
+{@link android.support.v4.app.NotificationCompat.Builder}. When you build
+notifications with this class, the system takes care of displaying
+notifications properly, whether they appear on a handheld or wearable.
+</p>
+
+<p class="note"><strong>Note:</strong>
+Notifications using {@link android.widget.RemoteViews} are stripped of custom
+layouts and the wearable only displays the text and icons. However, you can create
+<a href="{@docRoot}training/wearables/apps/layouts.html#CustomNotifications">create custom notifications</a>
+that use custom card layouts by creating a wearable app that runs on the wearable device.</p>
+</div>
+
+<h2 id="Import">Import the necessary classes</h2>
+<p>Before you begin, import the necessary classes from the support library:</p>
+
+<pre style="clear:right">
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.NotificationCompat.WearableExtender;
+</pre>
+
+<h2 id="NotificationBuilder">Create Notifications with the Notification Builder</h2>
+
+<p>The <a href="http://developer.android.com/tools/support-library/features.html#v4">v4
+support library</a> allows you to create notifications using the latest notification features
+such as action buttons and large icons, while remaining compatible with Android 1.6 (API level
+4) and higher.</p>
+
+<p>To create a notification with the support library, you create an instance of
+{@link android.support.v4.app.NotificationCompat.Builder} and issue the notification by
+passing it to {@link android.support.v4.app.NotificationManagerCompat#notify notify()}. For example:
+</p>
+
+<pre>
+int notificationId = 001;
+// Build intent for notification content
+Intent viewIntent = new Intent(this, ViewEventActivity.class);
+viewIntent.putExtra(EXTRA_EVENT_ID, eventId);
+PendingIntent viewPendingIntent =
+        PendingIntent.getActivity(this, 0, viewIntent, 0);
+
+NotificationCompat.Builder notificationBuilder =
+        new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.ic_event)
+        .setContentTitle(eventTitle)
+        .setContentText(eventLocation)
+        .setContentIntent(viewPendingIntent);
+
+// Get an instance of the NotificationManager service
+NotificationManagerCompat notificationManager =
+        NotificationManagerCompat.from(this);
+
+// Build the notification and issues it with notification manager.
+notificationManager.notify(notificationId, notificationBuilder.build());
+</pre>
+
+<p>When this notification appears on a handheld device, the user can invoke the
+{@link android.app.PendingIntent}
+specified by the {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent
+setContentIntent()} method by touching the notification. When this
+notification appears on an Android wearable, the user can swipe the notification to the left to
+reveal the <strong>Open</strong> action, which invokes the intent on the handheld device.</p>
+
+
+<img src="{@docRoot}wear/images/circle_email_action.png" height="200"
+  style="float:right;clear:right;margin:0 0 20px 60px" />
+
+<h2 id="ActionButtons">Add Action Buttons</h2>
+
+<p>In addition to the primary content action defined by
+{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent
+setContentIntent()}, you can add other actions by passing a {@link android.app.PendingIntent} to
+the {@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} method.</p>
+
+<p>For example, the following code shows the same type of notification from above, but adds an
+action to view the event location on a map.</p>
+
+<pre style="clear:right">
+// Build an intent for an action to view a map
+Intent mapIntent = new Intent(Intent.ACTION_VIEW);
+Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
+mapIntent.setData(geoUri);
+PendingIntent mapPendingIntent =
+        PendingIntent.getActivity(this, 0, mapIntent, 0);
+
+NotificationCompat.Builder notificationBuilder =
+        new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.ic_event)
+        .setContentTitle(eventTitle)
+        .setContentText(eventLocation)
+        .setContentIntent(viewPendingIntent)
+        <b>.addAction(R.drawable.ic_map,
+                getString(R.string.map), mapPendingIntent);</b>
+</pre>
+
+<p>On a handheld, the action appears as an
+additional button attached to the notification. On a wearable, the action appears as
+a large button when the user swipes the notification to the left. When the user taps the action,
+the associated intent is invoked on the handheld.</p>
+
+<p class="note"><strong>Tip:</strong> If your notifications include a "Reply" action
+  (such as for a messaging app), you can enhance the behavior by enabling
+  voice input replies directly from the Android wearable. For more information, read
+  <a href="{@docRoot}training/wearables/notifications/voice-input.html">Receiving Voice Input from
+  a Notification</a>.
+</p>
+
+<h2 id="SpecifyWearableOnlyActions">Specify Wearable-only Actions</h2>
+
+<p>
+If you want the actions available on the wearable to be different from those on the handheld,
+then use {@link android.support.v4.app.NotificationCompat.WearableExtender#addAction WearableExtender.addAction()}.
+Once you add an action with this method, the wearable does not display any other actions added with
+{@link android.support.v4.app.NotificationCompat.Builder#addAction NotificationCompat.Builder.addAction()}.
+That is, only the actions added with {@link android.support.v4.app.NotificationCompat.WearableExtender#addAction WearableExtender.addAction()} appear on the wearable and they do not appear on the handheld.
+</p>
+
+<pre>
+// Create an intent for the reply action
+Intent actionIntent = new Intent(this, ActionActivity.class);
+PendingIntent actionPendingIntent =
+        PendingIntent.getActivity(this, 0, actionIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+// Create the action
+NotificationCompat.Action action =
+        new NotificationCompat.Action.Builder(R.drawable.ic_action,
+                getString(R.string.label, actionPendingIntent))
+                .build();
+
+// Build the notification and add the action via WearableExtender
+Notification notification =
+        new NotificationCompat.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_message)
+                .setContentTitle(getString(R.string.title))
+                .setContentText(getString(R.string.content))
+                .extend(new WearableExtender().addAction(action))
+                .build();
+</pre>
+<h2 id="BigView">Add a Big View</h2>
+
+<img src="{@docRoot}wear/images/06_images.png" height="200"
+  style="float:right;margin:0 0 20px 40px" />
+
+<p>You can insert extended text content
+to your notification by adding one of the "big view" styles to your notification. On a
+handheld device, users can see the big view content by expanding the notification. On
+a wearable device, the big view content is visible by default.</p>
+
+<p>To add the extended content to your notification, call {@link
+android.support.v4.app.NotificationCompat.Builder#setStyle setStyle()} on the {@link
+android.support.v4.app.NotificationCompat.Builder} object, passing it an instance of either
+{@link android.support.v4.app.NotificationCompat.BigTextStyle BigTextStyle} or
+{@link android.support.v4.app.NotificationCompat.InboxStyle InboxStyle}.</p>
+
+<p>For example, the following code adds an instance of
+{@link android.support.v4.app.NotificationCompat.BigTextStyle} to the event notification,
+in order to include the complete event description (which includes more text than can fit
+into the space provided for {@link android.support.v4.app.NotificationCompat.Builder#setContentText
+setContentText()}).</p>
+
+<pre style="clear:right">
+// Specify the 'big view' content to display the long
+// event description that may not fit the normal content text.
+BigTextStyle bigStyle = new NotificationCompat.BigTextStyle();
+bigStyle.bigText(eventDescription);
+
+NotificationCompat.Builder notificationBuilder =
+        new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.ic_event)
+        .setLargeIcon(BitmapFractory.decodeResource(
+                getResources(), R.drawable.notif_background))
+        .setContentTitle(eventTitle)
+        .setContentText(eventLocation)
+        .setContentIntent(viewPendingIntent)
+        .addAction(R.drawable.ic_map,
+                getString(R.string.map), mapPendingIntent)
+        <b>.setStyle(bigStyle);</b>
+</pre>
+
+<p>Notice that you can add a large background image to any notification using the
+{@link android.support.v4.app.NotificationCompat.Builder#setLargeIcon setLargeIcon()}
+method. For more information about designing notifications with large images, see the
+<a href="{@docRoot}design/wear/index.html">Design Principles of Android
+Wear</a>.</p>
+
+<h2 id="AddWearableFeatures">Add Wearable Features For a Notification</h2>
+
+<p>If you ever need to add wearable-specific options to a notification, such as specifying additional
+pages of content or letting users dictate a text response with voice input, you can use the
+{@link android.support.v4.app.NotificationCompat.WearableExtender} class to
+specify the options. To use this API:</p>
+
+<ol>
+  <li>Create an instance of a {@link android.support.v4.app.NotificationCompat.WearableExtender WearableExtender},
+  setting the wearable-specific options for the notication.</li>
+  <li>Create an instance of
+  {@link android.support.v4.app.NotificationCompat.Builder}, setting the
+  desired properties for your notification as described earlier in this lesson.</li>
+  <li>Call {@link android.support.v4.app.NotificationCompat.Builder#extend extend()} on
+  the notification and pass in the
+  {@link android.support.v4.app.NotificationCompat.WearableExtender WearableExtender}. This applies
+  the wearable options to the notification.</li>
+  <li>Call {@link android.support.v4.app.NotificationCompat.Builder#build} to build the notification.</li>
+</ol>
+
+<p>
+For example, the following code calls the
+{@link android.support.v4.app.NotificationCompat.WearableExtender#setHintHideIcon setHintHideIcon()}
+method to remove the app icon from the notification card.
+</p>
+
+<pre>
+// Create a WearableExtender to add functionality for wearables
+NotificationCompat.WearableExtender wearableExtender =
+        new NotificationCompat.WearableExtender()
+        .setHintHideIcon(true);
+
+// Create a NotificationCompat.Builder to build a standard notification
+// then extend it with the WearableExtender
+Notification notif = new NotificationCompat.Builder(mContext)
+        .setContentTitle("New mail from " + sender)
+        .setContentText(subject)
+        .setSmallIcon(R.drawable.new_mail);
+        .extend(wearableExtender)
+        .build();
+</pre>
+
+<p>The
+  {@link android.support.v4.app.NotificationCompat.WearableExtender#setHintHideIcon setHintHideIcon()}
+  method is just one example of new notification features available with
+  {@link android.support.v4.app.NotificationCompat.WearableExtender}.
+</p>
+
+<p>If you ever need to read wearable-specifc options at a later time, use the corresponding get
+method for the option. This example calls the
+{@link android.support.v4.app.NotificationCompat.WearableExtender#getHintHideIcon()} method to
+get whether or not this notification hides the icon:
+<pre>
+NotificationCompat.WearableExtender wearableExtender =
+        new NotificationCompat.WearableExtender(notif);
+boolean hintHideIcon = wearableExtender.getHintHideIcon();
+</pre>
+
+<h2 id="Deliver">Deliver the Notification</h2>
+<p>When you want to deliver your notifications, always use the
+  {@link android.support.v4.app.NotificationManagerCompat} API instead of
+  {@link android.app.NotificationManager}:</p>
+
+<pre>
+// Get an instance of the NotificationManager service
+NotificationManagerCompat notificationManager =
+        NotificationManagerCompat.from(mContext);
+
+// Issue the notification with notification manager.
+notificationManager.notify(notificationId, notif);
+</pre>
+
+<p>If you use the framework's {@link android.app.NotificationManager}, some
+features from {@link android.support.v4.app.NotificationCompat.WearableExtender}
+do not work, so make sure to use {@link android.support.v4.app.NotificationCompat}.
+</p>
+
+<pre>
+NotificationCompat.WearableExtender wearableExtender =
+        new NotificationCompat.WearableExtender(notif);
+boolean hintHideIcon = wearableExtender.getHintHideIcon();
+ </pre>
+
+<p>The {@link android.support.v4.app.NotificationCompat.WearableExtender} APIs allow you to add
+additional pages to notifications, stack notifications, and more. Continue to the following lessons
+to learn about these features.
+</p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/index.jd b/docs/html/training/wearables/notifications/index.jd
new file mode 100644
index 0000000..17f3cb3
--- /dev/null
+++ b/docs/html/training/wearables/notifications/index.jd
@@ -0,0 +1,51 @@
+page.title=Adding Wearable Features to Notifications
+@jd:body
+
+<div id="tb-wrapper">
+  <div id="tb">
+
+    <h2>Dependencies and prerequisites</h2>
+    <ul>
+      <li>Android 4.3 (API Level 18) or higher on the handset device</li>
+      <li>An Android Wear device or Wear AVD</li>
+      <li><a href="{@docRoot}tools/support-library/features.html#v4">The Android v4 support library
+      (or v13, which includes v4)</li>
+    </ul>
+    <h2>You should also read</h2>
+    <ul>
+      <li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
+    </ul>
+  </div>
+</div>
+
+<p>When an Android handheld (phone or tablet) and Android wearable are connected, the handheld
+automatically shares notifications with the wearable. On the wearable, each
+notification appears as a new card in the <a href="{@docRoot}design/wear/index.html"
+>context stream</a>.</p>
+
+<p>However, to give users the best experience, you should add wearable-specific functionality to the
+notifications you already create. The following lessons show you how to
+create notifications that are catered for handhelds and wearables at the same time.
+</p>
+
+<img src="{@docRoot}wear/images/notification_phone@2x.png" width="700" height="265" />
+
+<p class="caption"><b>Figure 1.</b> The same notification displayed on a handheld and wearable.</p>
+
+<h2>Lessons</h2>
+  <dl>
+    <dt><a href="{@docRoot}training/wearables/notifications/creating.html">Creating a notification</a></dt>
+      <dd>Learn how to create notifications with wearable features with the Android support library.</dd>
+    <dt><a href="{@docRoot}training/wearables/notifications/voice-input.html">Receiving Voice Input
+in a Notification</a></dt>
+      <dd>Learn how to add an action to a wearable notification that receives voice input from users and delivers
+      the transcribed message to your handset app.</dd>
+    <dt><a href="{@docRoot}training/wearables/notifications/pages.html">Adding Pages to a Notification</a></dt>
+      <dd>Learn how to add additional pages of information that are visible when the user
+swipes to the left.</dd>
+    <dt><a href="{@docRoot}training/wearables/notifications/stacks.html">Stacking Notifications</a></dt>
+      <dd>Learn how to place all similar notifications from your app in a stack, allowing users to view
+      each notification individually without adding multiple cards to the card stream.</dd>
+  </dl>
+
+</div>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/pages.jd b/docs/html/training/wearables/notifications/pages.jd
new file mode 100644
index 0000000..1026774
--- /dev/null
+++ b/docs/html/training/wearables/notifications/pages.jd
@@ -0,0 +1,71 @@
+page.title=Adding Pages to a Notification
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li>Add Pages to a Notification</li>
+</ol>
+</div>
+</div>
+
+<p>When you'd like to provide more information without requiring users
+to open your app on their handheld device, you can
+add one or more pages to the notification on the wearable. The additional pages
+appear immediately to the right of the main notification card.
+</p>
+
+<img src="{@docRoot}wear/images/09_pages.png" height="200" style="margin:0 0 20px 0" />
+<img src="{@docRoot}wear/images/08_pages.png" height="200" style="margin:0 0 20px 40px" />
+
+<p>To create a notification with multiple pages:</p>
+<ol>
+    <li>Create the main notification (the first page) with
+    {@link android.support.v4.app.NotificationCompat.Builder},
+    in the way you'd like the notification to appear on a handset.</li>
+    <li>Create the additional pages for the wearable with
+    {@link android.support.v4.app.NotificationCompat.Builder}.</li>
+    <li>Apply the pages to the main notification with the
+    {@link android.support.v4.app.NotificationCompat.WearableExtender#addPage addPage()}
+    method or add multiple pages in a {@link java.util.Collection} with the
+    {@link android.support.v4.app.NotificationCompat.WearableExtender#addPage addPages()} method.
+    </li>
+</ol>
+
+<p>For example, here's some code that adds a second page to a notification:</p>
+
+<pre>
+// Create builder for the main notification
+NotificationCompat.Builder notificationBuilder =
+        new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.new_message)
+        .setContentTitle("Page 1")
+        .setContentText("Short message")
+        .setContentIntent(viewPendingIntent);
+
+// Create a big text style for the second page
+BigTextStyle secondPageStyle = new NotificationCompat.BigTextStyle();
+secondPageStyle.setBigContentTitle("Page 2")
+               .bigText("A lot of text...");
+
+// Create second page notification
+Notification secondPageNotification =
+        new NotificationCompat.Builder(this)
+        .setStyle(secondPageStyle)
+        .build();
+
+// Add second page with wearable extender and extend the main notification
+Notification twoPageNotification =
+        new WearableExtender()
+                .addPage(secondPageNotification)
+                .extend(notificationBuilder)
+                .build();
+
+// Issue the notification
+    notificationManager =
+            NotificationManagerCompat.from(this);
+    notificationManager.notify(notificationId, twoPageNotification);
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/stacks.jd b/docs/html/training/wearables/notifications/stacks.jd
new file mode 100644
index 0000000..e71e74c
--- /dev/null
+++ b/docs/html/training/wearables/notifications/stacks.jd
@@ -0,0 +1,154 @@
+page.title=Stacking Notifications
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#AddGroup">Add Each Notification to a Group</a></li>
+  <li><a href="#AddSummary">Add a Summary Notification</a></li>
+</ol>
+
+</div>
+</div>
+<img src="/wear/images/11_bundles_B.png" height="200" width="169"
+    style="margin:0 0 20px 20px; clear:both; float:right" alt="">
+<img src="/wear/images/11_bundles_A.png" height="200" width="169"
+    style="margin:0 20px 20px 20px; float:right" alt="">
+<p>When creating notifications for a handheld device, you should always aggregate similar
+notifications into a single summary notification. For example, if your app creates notifications
+for received messages, you should not show more than one notification
+on a handheld device&mdash;when more than one is message is received, use a single notification
+to provide a summary such as "2 new messages."</p>
+
+<p>However, a summary notification is less useful on an Android wearable because users
+are not able to read details from each message on the wearable (they must open your app on the
+handheld to view more information). So for the wearable device, you should
+group all the notifications together in a stack. The stack of notifications appears as a single
+card, which users can expand to view the details from each notification separately. The new
+{@link android.support.v4.app.NotificationCompat.Builder#setGroup setGroup()} method makes this
+possible while allowing you to still provide only one summary notification on the handheld device.</p>
+
+<h2 id="AddGroup" style="clear:right">Add Each Notification to a Group</h2>
+
+<p>To create a stack, call {@link android.support.v4.app.NotificationCompat.Builder#setGroup setGroup()}
+for each notification you want in the stack and specify a
+group key. Then call {@link android.support.v4.app.NotificationManagerCompat#notify notify()}
+to send it to the wearable.</p>
+
+<pre style="clear:right">
+final static String GROUP_KEY_EMAILS = "group_key_emails";
+
+// Build the notification, setting the group appropriately
+Notification notif = new NotificationCompat.Builder(mContext)
+         .setContentTitle("New mail from " + sender1)
+         .setContentText(subject1)
+         .setSmallIcon(R.drawable.new_mail);
+         .setGroup(GROUP_KEY_EMAILS)
+         .build();
+
+// Issue the notification
+NotificationManagerCompat notificationManager =
+        NotificationManagerCompat.from(this);
+notificationManager.notify(notificationId1, notif);
+</pre>
+
+<p>Later on, when you create another notification, specify
+the same group key. When you call
+{@link android.support.v4.app.NotificationManagerCompat#notify notify()},
+this notification appears in the same stack as the previous notification,
+instead of as a new card:</p>
+
+<pre style="clear:right">
+Notification notif2 = new NotificationCompat.Builder(mContext)
+         .setContentTitle("New mail from " + sender2)
+         .setContentText(subject2)
+         .setSmallIcon(R.drawable.new_mail);
+         .setGroup(GROUP_KEY_EMAILS)
+         .build();
+
+notificationManager.notify(notificationId2, notif2);
+</pre>
+
+<p>By default, notifications appear in the order in which you added them, with the most recent
+  notification visible at the top.  You can order notifications in another fashion by calling
+  {@link android.support.v4.app.NotificationCompat.Builder#setSortKey setSortKey()}.</p>
+
+
+<h2 id="AddSummary">Add a Summary Notification</h2>
+
+<img src="{@docRoot}wear/images/notif_summary_framed.png" height="242" width="330" style="float:right;margin:0 0 20px 40px" alt="" />
+
+<p>It's important that you still provide a summary notification that appears on handheld devices.
+So in addition to adding each unique notification to the same stack group, also add a summary
+notification and call {@link android.support.v4.app.NotificationCompat.Builder#setGroupSummary setGroupSummary()}
+on the summary notification.</p>
+
+<p>This notification does not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.</p>
+
+<pre style="clear:right">
+Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
+        R.drawable.ic_large_icon);
+
+// Create an InboxStyle notification
+Notification summaryNotification = new NotificationCompat.Builder(mContext)
+        .setContentTitle("2 new messages")
+        .setSmallIcon(R.drawable.ic_small_icon)
+        .setLargeIcon(largeIcon)
+        .setStyle(new NotificationCompat.InboxStyle()
+                .addLine("Alex Faaborg   Check this out")
+                .addLine("Jeff Chang   Launch Party")
+                .setBigContentTitle("2 new messages")
+                .setSummaryText("johndoe@gmail.com"))
+        .setGroup(GROUP_KEY_EMAILS)
+        .setGroupSummary(true)
+        .build();
+
+notificationManager.notify(notificationId3, summaryNotification);
+</pre>
+
+<p>
+This notification uses {@link android.support.v4.app.NotificationCompat.InboxStyle},
+which gives you an easy way to create notifications for email or messaging apps.
+You can use this style, another one defined in {@link android.support.v4.app.NotificationCompat},
+or no style for the summary notification.
+</p>
+
+<p class="note"><b>Tip:</b>
+To style the text like in the example screenshot, see
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithHTML">Styling
+with HTML markup</a> and
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithSpannables">Styling
+with Spannables</a>.
+</p>
+
+<p>Summary notifications can also affect notifications on wearables without being displayed on them.
+When creating a summary notification, you can use the
+{@link android.support.v4.app.NotificationCompat.WearableExtender} class and call
+{@link android.support.v4.app.NotificationCompat.WearableExtender#setBackground setBackground()} or
+{@link android.support.v4.app.NotificationCompat.WearableExtender#addAction addAction()} to set
+a background image or an action that applies to the entire stack on the wearable. For instance,
+to set the background for an entire stack of notifications:
+</p>
+
+<pre>
+Bitmap background = BitmapFactory.decodeResource(getResources(),
+        R.drawable.ic_background);
+
+NotificationCompat.WearableExtender wearableExtender =
+        new NotificationCompat.WearableExtender()
+        .setBackground(background);
+
+// Create an InboxStyle notification
+Notification summaryNotificationWithBackground =
+        new NotificationCompat.Builder(mContext)
+        .setContentTitle("2 new messages")
+        ...
+        .extend(wearableExtender)
+        .setGroup(GROUP_KEY_EMAILS)
+        .setGroupSummary(true)
+        .build();
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/voice-input.jd b/docs/html/training/wearables/notifications/voice-input.jd
new file mode 100644
index 0000000..4a27826
--- /dev/null
+++ b/docs/html/training/wearables/notifications/voice-input.jd
@@ -0,0 +1,175 @@
+page.title=Receiving Voice Input in a Notification
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#VoiceInput">Define the Voice Input</a></li>
+  <li><a href="#AddAction">Add the Voice Input as a Notification Action</li>
+  <li><a href="#ReceiveInput">Receiving the Voice Input as a String</a>
+</ol>
+
+</div>
+</div>
+
+<p>If you have handheld notifications that include an action to input text,
+such as reply to an email, it should normally launch an activity
+on the handheld device to input the text. However, when your notification appears on a wearable,
+there is no keyboard input, so you can let users dictate a reply or provide pre-defined text messages
+using {@link android.support.v4.app.RemoteInput}.
+</p>
+
+<p>When users reply with voice or select one of the available
+messages, the system attaches the text response to the {@link android.content.Intent} you specified
+for the notification action and sends that intent to your handheld app.</p>
+
+<img src="{@docRoot}wear/images/13_voicereply.png" height="200" width="169"
+style="float:right;margin:0 0 20px 40px;clear:right" />
+<img src="{@docRoot}wear/images/03_actions.png" height="200" width="169"
+style="float:right;margin:0 0 20px 40px" />
+
+<p class="note"><strong>Note:</strong> The Android emulator does not support voice input. When
+using the emulator for a wearable device, enable <b>Hardware keyboard present</b> in the AVD settings
+so you can type replies instead.</p>
+
+<h2 id="VoiceInput">Define the Voice Input</h2>
+
+<p>To create an action that supports voice input, create an instance of 
+  {@link android.support.v4.app.RemoteInput.Builder} that you can add to your notification action.
+  This class's constructor accepts a string that the system uses as
+  the key for the voice input, which you'll later use to retrieve the text of the
+  input in your handheld app.</p>
+
+<p>For example, here's how to create a
+{@link android.support.v4.app.RemoteInput} object that provides a custom
+label for the voice input prompt:</p>
+
+<pre class="prettyprint">
+// Key for the string that's delivered in the action's intent
+private static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
+
+String replyLabel = getResources().getString(R.string.reply_label);
+
+RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
+        .setLabel(replyLabel)
+        .build();
+</pre>
+
+
+<h3>Add Pre-defined Text Responses</h3>
+
+<img src="{@docRoot}wear/images/12_voicereply.png" height="200"
+style="float:right;margin:0 0 20px 40px" />
+
+<p>In addition to allowing voice input, you can
+    provide up to five text responses that the user can select for quick replies. Call
+ {@link android.support.v4.app.RemoteInput.Builder#setChoices setChoices()} and pass it a string array.</p>
+
+<p>For example, you can define some responses in a resource array:</p>
+
+<p class="code-caption">res/values/strings.xml</code>
+<pre class="prettyprint">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;resources>
+    &lt;string-array name="reply_choices">
+        &lt;item>Yes&lt;/item>
+        &lt;item>No&lt;/item>
+        &lt;item>Maybe&lt;/item>
+    &lt;/string-array>
+&lt;/resources>
+</pre>
+
+<p>Then, inflate the string array and add it to the
+ {@link android.support.v4.app.RemoteInput}:</p>
+
+<pre>
+public static final EXTRA_VOICE_REPLY = "extra_voice_reply";
+...
+String replyLabel = getResources().getString(R.string.reply_label);
+String[] replyChoices = getResources().getStringArray(R.array.reply_choices);
+
+RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
+        .setLabel(replyLabel)
+        .setChoices(replyChoices)
+        .build();
+</pre>
+
+<h2 id="AddAction">Add the Voice Input as a Notification Action</h2>
+
+<p>
+To set the voice input, attach your
+{@link android.support.v4.app.RemoteInput} object to an action using
+{@link android.support.v4.app.NotificationCompat.Action.Builder#addRemoteInput addRemoteInput()}.
+You can then apply the action to the notification. For example:
+</p>
+
+<pre>
+// Create an intent for the reply action
+Intent replyIntent = new Intent(this, ReplyActivity.class);
+PendingIntent replyPendingIntent =
+        PendingIntent.getActivity(this, 0, replyIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+// Create the reply action and add the remote input
+NotificationCompat.Action action =
+        new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
+                getString(R.string.label, replyPendingIntent))
+                .addRemoteInput(remoteInput)
+                .build();
+
+// Build the notification and add the action via WearableExtender
+Notification notification =
+        new NotificationCompat.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_message)
+                .setContentTitle(getString(R.string.title))
+                .setContentText(getString(R.string.content))
+                .extend(new WearableExtender().addAction(action))
+                .build();
+
+// Issue the notification
+NotificationManagerCompat notificationManager =
+        NotificationManagerCompat.from(mContext);
+notificationManager.notify(notificationId, notification);
+</pre>
+<p>
+When you issue this notification, users can swipe to the left to see the "Reply" action button.
+</p>
+
+<h2 id="ReceiveInput">Receiving the Voice Input as a String</h2>
+<p>
+To receive the user's transcribed message in the activity you declared in the reply action's intent,
+call {@link android.support.v4.app.RemoteInput#getResultsFromIntent getResultsFromIntent()},
+passing in the "Reply" action's intent. This method returns a {@link android.os.Bundle} that
+contains the text response. You can then query the {@link android.os.Bundle} to obtain the response.
+
+<p class="note"><b>Note:</b> Do not use {@link android.content.Intent#getExtras Intent.getExtras()}
+to obtain the voice result, because the voice input is stored as
+{@link android.content.ClipData}. The {@link android.support.v4.app.RemoteInput#getResultsFromIntent
+getResultsFromIntent()} method provides a convenient way to receive a character sequence without
+having to process the {@link android.content.ClipData} yourself.
+</p>
+
+<p>
+The following code shows a method that accepts an intent and returns the voice response,
+which is referenced by the <code>EXTRA_VOICE_REPLY</code> key that is used in the previous examples:
+</p>
+
+<pre>
+/**
+ * Obtain the intent that started this activity by calling
+ * Activity.getIntent() and pass it into this method to
+ * get the associated voice input string.
+ */
+
+private CharSequence getMessageText(Intent intent) {
+    Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+        if (remoteInput != null) {
+            return remoteInput.getCharSequence(EXTRA_VOICE_REPLY);
+        }
+    }
+    return null;
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/tv/images/atv-framed.png b/docs/html/tv/images/atv-framed.png
new file mode 100644
index 0000000..4cedab2
--- /dev/null
+++ b/docs/html/tv/images/atv-framed.png
Binary files differ
diff --git a/docs/html/tv/images/components.png b/docs/html/tv/images/components.png
new file mode 100644
index 0000000..5984c64
--- /dev/null
+++ b/docs/html/tv/images/components.png
Binary files differ
diff --git a/docs/html/tv/images/hero.jpg b/docs/html/tv/images/hero.jpg
index e951167..e9f8c47 100644
--- a/docs/html/tv/images/hero.jpg
+++ b/docs/html/tv/images/hero.jpg
Binary files differ
diff --git a/docs/html/tv/images/recommendations.png b/docs/html/tv/images/recommendations.png
new file mode 100644
index 0000000..e7aa01d
--- /dev/null
+++ b/docs/html/tv/images/recommendations.png
Binary files differ
diff --git a/docs/html/tv/images/search.png b/docs/html/tv/images/search.png
new file mode 100644
index 0000000..92e1e5f
--- /dev/null
+++ b/docs/html/tv/images/search.png
Binary files differ
diff --git a/docs/html/tv/index.jd b/docs/html/tv/index.jd
index 5c48e49..e3f3855 100644
--- a/docs/html/tv/index.jd
+++ b/docs/html/tv/index.jd
@@ -27,11 +27,11 @@
             <div class="col-10">
               <div class="landing-section-header">
                 <div class="landing-h1 hero">Android TV</div>
-                <div class="landing-subhead hero">Your apps on the big screen</div>
+                <div class="landing-subhead hero">Big screen apps, games, and content</div>
                 <div class="landing-hero-description">
-                  <p>Engage users from the comfort of their couches.
-                    Put your app on TV and bring everyone into
-                    the action.</p>
+                  <p>Recommend great content to users right on the home screen.
+                    Enable users to find movies through voice search.
+                    Engage users with fluid, immersive games.</p>
                 </div>
               </div>
 
@@ -56,6 +56,7 @@
 
       <div class="landing-section" style="background-color:#f5f5f5" id="reimagine-your-app">
         <div class="wrap">
+
           <div class="landing-section-header">
             <div class="landing-h1">Reimagine Your App</div>
             <div class="landing-subhead">
@@ -63,50 +64,19 @@
             </div>
           </div>
 
-          <div class="landing-body">
-            <div class="landing-breakout cols">
-
-              <div class="col-3-wide">
-                <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
-
-                <p>Simple</p>
-                <p class="landing-small">
-                  Smooth, fast interactions are key to a successful TV app. Keep navigation simple
-                  and light. Bring your content forward to let users enjoy it with a minimum of
-                  fuss.
-                </p>
-                <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about TV design</a>
-                </p>
-              </div>
-
-              <div class="col-3-wide">
-                <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
-
-                <p>Cinematic</p>
-                <p class="landing-small">
-                  What would your app look like if it were a film? Use movement, animation and sound to make your app into an experience.
-                </p>
-                <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about TV design</a>
-                </p>
-              </div>
-
-              <div class="col-3-wide">
-                <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
-
-                <p>Beautiful</p>
-                <p class="landing-small">
-                  Apps on TV should be a pleasure to look at, as well as enjoyable to use. Use
-                  made-for-TV styles to make your app familiar and fun.
-                </p>
-                <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about design for TV</a>
-                </p>
-              </div>
-            </div>
-
+          <div class="landing-body landing-align-center">
+            <img src="{@docRoot}tv/images/atv-framed.png" alt="Android TV" >
+            <p>Simple. Cinematic. Beautiful.</p>
+            <p class="landing-small">
+              Smooth, fast interactions are key to a successful TV app. Keep navigation simple
+              and light. <br>Bring your content forward to let users enjoy it with a minimum of
+              effort.
+            </p>
+            <p class="landing-small">
+              <a href="/design/tv/index.html">Learn about design for TV</a>
+            </p>
           </div>
+
         </div>  <!-- end .wrap -->
       </div>  <!-- end .landing-section -->
 
@@ -125,7 +95,8 @@
             <div class="landing-breakout cols">
 
               <div class="col-3-wide">
-                <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
+                <img src="{@docRoot}tv/images/components.png" alt="TV layout components"
+                  style="margin-left: 23px;">
 
                 <p>Made for TV</p>
                 <p class="landing-small">
@@ -138,7 +109,8 @@
               </div>
 
               <div class="col-3-wide">
-                <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
+                <img src="{@docRoot}tv/images/search.png" alt="Search"
+                  style="margin-left: 23px;">
 
                 <p>Get Found</p>
                 <p class="landing-small">
@@ -150,7 +122,8 @@
               </div>
 
               <div class="col-3-wide">
-                <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
+                <img src="{@docRoot}tv/images/recommendations.png" alt="Recommendations"
+                  style="margin-left: 23px;">
 
                 <p>Recommend</p>
                 <p class="landing-small">
@@ -168,7 +141,7 @@
         </div>  <!-- end .wrap -->
       </div> <!-- end .landing-section -->
 
-      <div class="landing-section landing-red-background">
+      <div class="landing-section landing-red-background" id="start">
         <div class="wrap">
           <div class="landing-section-header">
             <div class="landing-h1 landing-align-left">Get Started with Android TV</div>
@@ -180,10 +153,10 @@
           <div class="landing-body">
             <div class="landing-breakout cols">
               <div class="col-8" style="margin-left: -8px;">
-                <p style="font-size: 24px;">L-Preview SDK</p>
+                <p style="font-size: 24px;">L Developer Preview</p>
                 <p>
-                  The preview SDK includes all the tools you need to build and test apps for TV.
-                  Download it and start creating your big-screen app.
+                   Get all the tools you need to build and test TV apps. Download the preview and
+                   start creating your big-screen experience.
                 </p>
 
               </div>
@@ -191,8 +164,9 @@
               <div class="col-8">
                 <p style="font-size: 24px;">ADT-1 Developer Kit</p>
                 <p>
-                  Request an ADT-1 Developer Kit, a compact and powerful streaming media player
-                  and gamepad, ideal for developing and testing apps for TV.
+                  Request an <a href="{@docRoot}preview/tv/adt-1/index.html"
+                  style="color: white;"><u>ADT-1 Developer Kit</u></a>, a compact and powerful
+                  streaming media player and gamepad, ideal for developing and testing apps for TV.
                 </p>
 
               </div>
@@ -204,14 +178,15 @@
             <div class="landing-breakout cols">
 
               <div class="col-8">
-                <a href="{@docRoot}preview/setup-sdk.html" class="landing-button landing-primary">
-                  Download the Preview SDK
+                <a href="{@docRoot}preview/tv/start/index.html" class="landing-button landing-primary">
+                  Get Started
                 </a>
               </div>
 
               <div class="col-8">
-                <a href="{@docRoot}preview/tv/adt-1/request.html" class="landing-button landing-primary">
-                  Request ADT-1 Developer Kit
+                <a href="https://support.google.com/googleplay/android-developer/contact/adt_request"
+                  class="landing-button landing-primary">
+                  Request the ADT-1 Developer Kit
                 </a>
               </div>
             </div>
diff --git a/docs/html/wear/images/features/apps.png b/docs/html/wear/images/features/apps.png
new file mode 100644
index 0000000..dbbb5e5
--- /dev/null
+++ b/docs/html/wear/images/features/apps.png
Binary files differ
diff --git a/docs/html/wear/images/features/notifications.png b/docs/html/wear/images/features/notifications.png
new file mode 100644
index 0000000..128853c
--- /dev/null
+++ b/docs/html/wear/images/features/notifications.png
Binary files differ
diff --git a/docs/html/wear/images/features/s1.png b/docs/html/wear/images/features/s1.png
deleted file mode 100644
index ba96cf8..0000000
--- a/docs/html/wear/images/features/s1.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/s2.png b/docs/html/wear/images/features/s2.png
deleted file mode 100644
index af28496..0000000
--- a/docs/html/wear/images/features/s2.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/s3.png b/docs/html/wear/images/features/s3.png
deleted file mode 100644
index 6ae9868..0000000
--- a/docs/html/wear/images/features/s3.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/s4.png b/docs/html/wear/images/features/s4.png
deleted file mode 100644
index 125713d..0000000
--- a/docs/html/wear/images/features/s4.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/send-data.png b/docs/html/wear/images/features/send-data.png
new file mode 100644
index 0000000..7010e3f
--- /dev/null
+++ b/docs/html/wear/images/features/send-data.png
Binary files differ
diff --git a/docs/html/wear/images/features/ts1.png b/docs/html/wear/images/features/ts1.png
deleted file mode 100644
index 5d4b1c1..0000000
--- a/docs/html/wear/images/features/ts1.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/ts2.png b/docs/html/wear/images/features/ts2.png
deleted file mode 100644
index dc798c5..0000000
--- a/docs/html/wear/images/features/ts2.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/ts3.png b/docs/html/wear/images/features/ts3.png
deleted file mode 100644
index 0d68ebc..0000000
--- a/docs/html/wear/images/features/ts3.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/wear/images/features/ts4.png b/docs/html/wear/images/features/voice.png
similarity index 100%
rename from docs/html/wear/images/features/ts4.png
rename to docs/html/wear/images/features/voice.png
Binary files differ
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index bc08aa0..10e7d29 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -124,15 +124,15 @@
           <div class="landing-section-header">
             <div class="landing-h1">Developing for Android Wear</div>
             <div class="landing-subhead">
-             The Android Wear APIs are delivered in the Android v4 support library and Google Play services.
-             This lets Android handhelds, old and new, communicate with Android wearables.
-            </div>
+             The Android Wear APIs are delivered in the Android Support Library and Google Play
+             services. When using these libraries, handheld devices running Android 4.3
+             (API Level 18) or later can communicate with wearables.</div>
           </div>
 
           <div class="landing-body">
             <div class="landing-breakout cols">
               <div class="col-4">
-                <img src="{@docRoot}wear/images/features/ts2.png" alt="">
+                <img src="{@docRoot}wear/images/features/notifications.png" alt="">
                 <p>Synced Notifications</p>
                 <p class="landing-small">
                   Notifications on handhelds can automatically sync to wearables, so design them
@@ -143,31 +143,7 @@
                 </p>
               </div>
               <div class="col-4">
-                <img src="{@docRoot}wear/images/features/ts1.png" alt="">
-                <p>Wearable Apps</p>
-                <p class="landing-small">
-                  Create custom experiences with activities, services, sensors, and much
-                  more with the Android SDK.
-                </p>
-                <p class="landing-small">
-                  <a href="{@docRoot}training/wearables/apps/index.html">Create wearable apps</a>
-
-                </p>
-              </div>
-              <div class="col-4">
-                <img src="{@docRoot}wear/images/features/ts2.png" alt="">
-                <p>Send Data</p>
-                <p class="landing-small">
-                  Send data and actions between handhelds and wearables with
-                  data replication APIs and RPCs.
-                </p>
-                <p class="landing-small">
-                  <a href="{@docRoot}training/wearables/data-layer/index.html">Work with the Data Layer</a>
-
-                </p>
-              </div>
-              <div class="col-4">
-                <img src="{@docRoot}wear/images/features/ts4.png" alt="">
+                <img src="{@docRoot}wear/images/features/voice.png" alt="">
                 <p>Voice Actions</p>
                 <p class="landing-small">
                   Register your app to handle voice actions, like "Ok Google, take a&nbsp;note,"
@@ -177,6 +153,29 @@
                   <a href="{@docRoot}training/wearables/apps/voice-actions.html">Integrate voice actions</a>
                 </p>
               </div>
+              <div class="col-4">
+                <img src="{@docRoot}wear/images/features/apps.png" alt="">
+                <p>Build Wearable Apps</p>
+                <p class="landing-small">
+                  Create custom experiences with activities, services, sensors, and much
+                  more with the Android SDK.
+                </p>
+                <p class="landing-small">
+                  <a href="{@docRoot}training/wearables/apps/index.html">Create wearable apps</a>
+                </p>
+              </div>
+              <div class="col-4">
+                <img src="{@docRoot}wear/images/features/send-data.png" alt="">
+                <p>Synced Data</p>
+                <p class="landing-small">
+                  Send data and actions between handhelds and wearables with
+                  data replication APIs and RPCs.
+                </p>
+                <p class="landing-small">
+                  <a href="{@docRoot}training/wearables/data-layer/index.html">Work with the Data Layer</a>
+
+                </p>
+              </div>
             </div>
           </div>
         </div> <!-- end .wrap -->
@@ -185,7 +184,7 @@
       <div class="landing-section landing-white-background">
         <div class="wrap">
           <div class="landing-section-header">
-            <div class="landing-h2">Building an Ecosystem</div>
+            <div class="landing-h1">Building an Ecosystem</div>
             <div class="landing-body landing-align-center">
               <p class="landing-small">
                 We’re working with partners around the world to build watches powered by Android Wear!
diff --git a/docs/html/wear/notifications/creating.jd b/docs/html/wear/notifications/creating.jd
deleted file mode 100644
index a5d7da7..0000000
--- a/docs/html/wear/notifications/creating.jd
+++ /dev/null
@@ -1,305 +0,0 @@
-page.title=Creating Notifications for Android Wear
-
-@jd:body
-
-
-<p>When an Android device such as a phone or tablet is connected to an Android wearable,
-all notifications are shared between the devices by default. On the Android wearable, each
-notification appears as a new card in the <a href="{@docRoot}wear/design/user-interface.html#Stream"
->context stream</a>.</p>
-
-<img src="{@docRoot}wear/images/notification_phone@2x.png" width="700" height="265" />
-
-
-<p>So without any effort, your app notifications are available to users on Android Wear.
-However, you can enhance the user experience in several ways. For instance,
-if users may respond to a notification by entering text, such as to reply to
-a message, you can add the ability for users to reply by voice directly from the
-wearable.</p>
-
-<p>To help you provide the best user experience
-for your notifications on Android Wear, this guide shows you how to
-build notifications using standard templates in
-the {@link android.support.v4.app.NotificationCompat.Builder} APIs, plus how to begin
-extending your notification's capabilities for the wearable user experience.</p>
-
-<p class="note"><strong>Note:</strong>
-Notifications using {@link android.widget.RemoteViews} are stripped of custom
-layouts and the system uses only the text and icons in the
-{@link android.app.Notification} object to
-display the notification in a card. However, custom card layouts will be supported by
-the official Android Wear SDK that is coming later.</p>
-</div>
-
-
-
-
-<h2 id="Import">Import the Necessary Classes</h2>
-
-<p>To begin development, you must first complete the instructions in the <a
-href="{@docRoot}wear/preview/start.html">Get Started with the Developer Preview</a> document.
-As mentioned in that document, your app must include
-both the <a href="http://developer.android.com/tools/support-library/features.html#v4">v4 support
-library</a> and the Developer Preview support library. So to get started,
-you should include the following imports in your project code:</p>
-
-<pre>
-import android.support.wearable.notifications.*;
-import android.support.wearable.app.NotificationManagerCompat;
-import android.support.v4.app.NotificationCompat;
-</pre>
-
-<p class="caution"><strong>Caution:</strong>
-The APIs in the current Android Wear Developer Preview are intended for <b>development and testing purposes only</b>, not for production apps. Google may change this Developer Preview significantly prior to the official release of the Android Wear SDK. You may not publicly distribute or ship any application using this Developer Preview, as this Developer Preview will no longer be supported after the official SDK is released (which will cause applications based only on the Developer Preview to break).</p>
-
-
-
-<h2 id="NotificationBuilder">Create Notifications with the Notification Builder</h2>
-
-<p>The <a href="http://developer.android.com/tools/support-library/features.html#v4">v4
-support library</a> allows you to create notifications using the latest notification features
-such as action buttons and large icons, while remaining compatible with Android 1.6 (API level
-4) and higher.</p>
-
-
-<p>For example, here's some code that creates and issues a notification using the
-{@link android.support.v4.app.NotificationCompat} APIs combined with the new
-<a href="{@docRoot}reference/android/support/wearable/app/NotificationManagerCompat.html">
-<code>NotificationManagerCompat</code></a> API:</p>
-
-
-<pre>
-int notificationId = 001;
-// Build intent for notification content
-Intent viewIntent = new Intent(this, ViewEventActivity.class);
-viewIntent.putExtra(EXTRA_EVENT_ID, eventId);
-PendingIntent viewPendingIntent =
-        PendingIntent.getActivity(this, 0, viewIntent, 0);
-
-NotificationCompat.Builder notificationBuilder =
-        new NotificationCompat.Builder(this)
-        .setSmallIcon(R.drawable.ic_event)
-        .setContentTitle(eventTitle)
-        .setContentText(eventLocation)
-        .setContentIntent(viewPendingIntent);
-
-// Get an instance of the NotificationManager service
-NotificationManagerCompat notificationManager =
-        NotificationManagerCompat.from(this);
-
-// Build the notification and issues it with notification manager.
-notificationManager.notify(notificationId, notificationBuilder.build());
-</pre>
-
-<p>When this notification appears on a handheld device, the user can invoke the
-{@link android.app.PendingIntent}
-specified by the {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent
-setContentIntent()} method by touching the notification. When this
-notification appears on an Android wearable, the user can swipe the notification to the left to
-reveal the <strong>Open</strong> action, which invokes the intent on the handheld device.</p>
-
-
-
-
-
-
-<img src="{@docRoot}wear/images/circle_email_action.png" height="200" style="float:right;clear:right;margin:0 0 20px 60px" />
-
-<h2 id="ActionButtons">Add Action Buttons</h2>
-
-<p>In addition to the primary content action defined by
-{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent
-setContentIntent()}, you can add other actions by passing a {@link android.app.PendingIntent} to
-the {@link android.support.v4.app.NotificationCompat.Builder#addAction
-addAction()} method.</p>
-
-<p>For example, the following code shows the same type of notification from above, but adds an
-action to view the event location on a map.</p>
-
-<pre style="clear:right">
-// Build an intent for an action to view a map
-Intent mapIntent = new Intent(Intent.ACTION_VIEW);
-Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
-mapIntent.setData(geoUri);
-PendingIntent mapPendingIntent =
-        PendingIntent.getActivity(this, 0, mapIntent, 0);
-
-NotificationCompat.Builder notificationBuilder =
-        new NotificationCompat.Builder(this)
-        .setSmallIcon(R.drawable.ic_event)
-        .setContentTitle(eventTitle)
-        .setContentText(eventLocation)
-        .setContentIntent(viewPendingIntent)
-        <b>.addAction(R.drawable.ic_map,
-                getString(R.string.map), mapPendingIntent);</b>
-</pre>
-
-<p>On a handheld device, the action appears as an
-additional button attached to the notification. On an Android wearable, the action appears as
-a large button when the user swipes the notification to the left. When the user taps the action,
-the associated {@link android.content.Intent} is invoked on the handheld device.</p>
-
-<p class="note"><strong>Tip:</strong> If your notifications includes a "Reply" action
-  (such as for a messaging app), you can enhance the behavior by enabling
-  voice input replies directly from the Android wearable. For more information, read
-  <a href="{@docRoot}wear/notifications/remote-input.html">Receiving Voice Input from a Notification</a>.
-</p>
-
-<p>For details about designing action buttons (including the icon specifications), see the
-<a href="{@docRoot}wear/design/index.html#NotifictionActions">Design Principles of Android
-Wear</a>.</p>
-
-
-<h2 id="BigView">Add a Big View</h2>
-
-<img src="{@docRoot}wear/images/06_images.png" height="200" style="float:right;margin:0 0 20px 40px" />
-
-<p>You can insert extended text content
-to your notification by adding one of the "big view" styles to your notification. On a
-handheld device, users can see the big view content by expanding the notification,
-while on Android Wear, the big view content is visible by default.</p>
-
-<p>To add the extended content to your notification, call {@link
-android.support.v4.app.NotificationCompat.Builder#setStyle setStyle()} on the {@link
-android.support.v4.app.NotificationCompat.Builder} object, passing it an instance of either
-{@link android.support.v4.app.NotificationCompat.BigTextStyle BigTextStyle} or
-{@link android.support.v4.app.NotificationCompat.InboxStyle InboxStyle}.</p>
-
-<p>For example, the following code adds an instance of
-{@link android.support.v4.app.NotificationCompat.BigTextStyle} to the event notification,
-in order to include the complete event description (which includes more text than can fit
-into the space provided for {@link android.support.v4.app.NotificationCompat.Builder#setContentText
-setContentText()}).</p>
-
-
-<pre style="clear:right">
-// Specify the 'big view' content to display the long
-// event description that may not fit the normal content text.
-BigTextStyle bigStyle = new NotificationCompat.BigTextStyle();
-bigStyle.bigText(eventDescription);
-
-NotificationCompat.Builder notificationBuilder =
-        new NotificationCompat.Builder(this)
-        .setSmallIcon(R.drawable.ic_event)
-        .setLargeIcon(BitmapFractory.decodeResource(
-                getResources(), R.drawable.notif_background))
-        .setContentTitle(eventTitle)
-        .setContentText(eventLocation)
-        .setContentIntent(viewPendingIntent)
-        .addAction(R.drawable.ic_map,
-                getString(R.string.map), mapPendingIntent)
-        <b>.setStyle(bigStyle);</b>
-</pre>
-
-<p>Notice that you can add a large background image to any notification using the
-{@link android.support.v4.app.NotificationCompat.Builder#setLargeIcon setLargeIcon()}
-method. For more information about designing notifications with large images, see the
-<a href="{@docRoot}wear/design/index.html#Images">Design Principles of Android
-Wear</a>.</p>
-
-
-
-<h2 id="NewFeatures">Add New Features for Wearables</h2>
-
-<p>The Android Wear preview support library provides new APIs that
-  allow you to enhance the user experience for notifications on a wearable device. For example,
-  you can add additional pages of content that users can view by swiping to the left, or add the ability
-for users to deliver your app a text response using voice input.</p>
-
-<p>To use these new APIs:</p>
-
-<ol>
-  <li>Create an instance of
-{@link android.support.v4.app.NotificationCompat.Builder}, setting the
-desired properties for your notification.</li>
-  <li>Create a
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#WearableNotificationOptions.Builder(android.content.Context)"> <code>WearableNotificationOptions.Builder</code></a>, setting the wearable-specific options for the notication.</li>
-  <li>Call <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#WearableNotificationOptions.Builder#applyTo"><code>WearableNotificationOptions.Builder.applyTo()</code>
-  </a>, passing in the {@link android.support.v4.app.NotificationCompat.Builder}. This applies
-  the wearable options to the notification.</li>
-</ol>
-
-<p>
-For example, the following code calls the
- <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#setHintHideIcon(boolean)">
-  <code>setHintHideIcon()</code></a> method to remove the app icon from the notification card.
-</p>
-
-<pre>
-// Create a NotificationCompat.Builder for standard notification features
- NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle("New mail from " + sender)
-         .setContentText(subject)
-         .setSmallIcon(R.drawable.new_mail);
-// Create a WearablesNotificationOptions.Builder to add functionality for wearables
- Notification notif = new WearableNotificationOptions.Builder()
-         <b>.setHintHideIcon(true)</b>
-         .build()
-         .applyTo(builder); //apply wearable options to to the original notification
-         .build()
-</pre>
-
-<p>The
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#setHintHideIcon(boolean)">
-  <code>setHintHideIcon()</code></a> method is just one example of new notification features available with the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html"
-  ><code>WearableNotificationOptions.Builder</code></a> class.
-</p>
-
-
-<p>When you want to deliver your notifications, always use the
-  <a href="{@docRoot}reference/android/support/wearable/app/NotificationManagerCompat.html">
-  <code>NotificationManagerCompat</code></a> API instead of
-  {@link android.app.NotificationManager}:</p>
-
-<pre>
-// Get an instance of the NotificationManager service
-NotificationManagerCompat notificationManager =
-        NotificationManagerCompat.from(this);
-
-// Issue the notification with notification manager.
-notificationManager.notify(notificationId, notif);
-</pre>
-
-
-<p>If you use the framework's {@link android.app.NotificationManager}, some
-features from <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html"><code>WearableNotificationOptions.Builder</code></a>
-do not work.</p>
-
-
-<p>To continue enhancing your notifications for wearables using
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html"
-  ><code>WearableNotificationOptions.Builder</code></a> and other APIs in the
-  preview support library, see the following developer guides:</p>
-
-  <dl>
-    <dt><a href="{@docRoot}wear/notifications/remote-input.html">Receiving Voice Input
-from a Notification</a></dt>
-      <dd>Add an action that receives voice input from the user and delivers the
-transcribed message to your app.</dd>
-    <dt><a href="{@docRoot}wear/notifications/pages.html">Adding Pages to a Notification</a></dt>
-      <dd>Add additional pages of information that are visible when the user
-swipes to the left.</dd>
-    <dt><a href="{@docRoot}wear/notifications/stacks.html">Stacking Notifications</a></dt>
-      <dd>Place all similar notifications from your app in a stack, allowing each to be
-viewed individually without adding multiple cards to the card stream.</dd>
-  </dl>
-
-
-<div class="next-docs">
-
-<div class="col-12">
-  <h2 class="norule">You might also want to read:</h2>
-  <dl>
-    <dt><a href="{@docRoot}training/notify-user/index.html">Notifying the User</a></dt>
-    <dd>Learn more about how to create notifications.</dd>
-    <dt><a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a></dt>
-    <dd>Learn everything you need to know about the {@link android.content.Intent}
-APIs, used by notificaton actions.</dd>
-  </dl>
-</div>
-</div>
-
-
-</body>
-</html>
diff --git a/docs/html/wear/notifications/pages.jd b/docs/html/wear/notifications/pages.jd
deleted file mode 100644
index 7d18b3f..0000000
--- a/docs/html/wear/notifications/pages.jd
+++ /dev/null
@@ -1,65 +0,0 @@
-page.title=Adding Pages to a Notification
-
-@jd:body
-
-
-<img src="{@docRoot}wear/images/09_pages.png" height="200" style="float:right;margin:0 0 20px 40px" />
-<img src="{@docRoot}wear/images/08_pages.png" height="200" style="float:right;margin:0 0 20px 40px" />
-
-<p>When you'd like to provide more information without requiring users
-to open your app on their handheld device, you can
-add one or more pages to the notification on Android Wear. The additional pages
-appear immediately to the right of the main notification card.
-For information about when to use and how to design
-multiple pages, see the
-<a href="{@docRoot}wear/design/index.html#NotificationPages">Design Principles of Android
-Wear</a>.</p>
-
-<p>To create a notification with multiple pages:</p>
-<ol>
-    <li>Create the main notification (the first page) the way you'd like the notification to appear on a phone
-    or tablet.</li>
-    <li>Add pages one at a time with the
-<a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#addPage(android.app.Notification)">
-<code>addPage()</code></a> method, or add multiple pages in a {@link java.util.Collection} with the
-<a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#addPages(java.util.Collection<android.app.Notification>)">
-<code>addPages()</code></a> method.</li>
-    <li>Apply the pages to the main notification with the
-    <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.html#applyTo(android.support.v4.app.NotificationCompat.Builder)"
-    ><code>applyTo()</code></a> method.</li>
-</ol>
-
-
-<p>For example, here's some code that adds a second page to a notification:</p>
-
-<pre>
-// Create builder for the main notification
-NotificationCompat.Builder notificationBuilder =
-        new NotificationCompat.Builder(this)
-        .setSmallIcon(R.drawable.new_message)
-        .setContentTitle("Page 1")
-        .setContentText("Short message")
-        .setContentIntent(viewPendingIntent);
-
-// Create a big text style for the second page
-BigTextStyle secondPageStyle = new NotificationCompat.BigTextStyle();
-secondPageStyle.setBigContentTitle("Page 2")
-               .bigText("A lot of text...");
-
-// Create second page notification
-Notification secondPageNotification =
-        new NotificationCompat.Builder(this)
-        .setStyle(secondPageStyle)
-        .build();
-
-// Add second page with wearable options and apply to main notification
-Notification twoPageNotification =
-        new WearableNotificationsOptions.Builder()
-        .addPage(secondPageNotification)
-        .build()
-        .applyTo(notificationBuilder)
-        .build();
-</pre>
-
-</body>
-</html>
diff --git a/docs/html/wear/notifications/remote-input.jd b/docs/html/wear/notifications/remote-input.jd
deleted file mode 100644
index 4db8274..0000000
--- a/docs/html/wear/notifications/remote-input.jd
+++ /dev/null
@@ -1,241 +0,0 @@
-page.title=Receiving Voice Input from a Notification
-
-@jd:body
-
-<img src="{@docRoot}wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
-
-<img src="{@docRoot}wear/images/03_actions.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
-
-<p>If your notification includes an action to respond with text,
-    such as to reply to an email, it should normally launch an activity
-    on the handheld device. However, when your notification appears on an Android wearable, you can
-    allow users to dictate a reply with voice input. You can also provide pre-defined text
-    messages for the user to select.</p>
-
-<p>When the user replies with voice or selects one of the available
-messages, the system sends the message to your app on the connected handheld device.
-The message is attached as an extra in the {@link android.content.Intent} you specified
-to be used for the notification action.</p>
-
-<p class="note"><strong>Note:</strong> When developing with the Android emulator,
-you must type text replies into the voice input field, so be sure you have enabled
-<strong>Hardware keyboard present</strong> in the AVD settings.</p>
-
-
-<h2 id="RemoteInput">Define the Remote Input</h2>
-
-<p>To create an action that supports voice input, first create an instance of
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html">
-<code>RemoteInput</code></a> using the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> APIs.
-    The
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> constructor takes a string that the system
-    will use as a key for the {@link android.content.Intent} extra that carries the reply message
-    to your app on the handheld.</p>
-
-<p>For example, here's how to create a new
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html">
-<code>RemoteInput</code></a> object that provides a custom
-    label for the voice input prompt:</p>
-
-<pre class="prettyprint">
-// Key for the string that's delivered in the action's intent
-private static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
-
-String replyLabel = getResources().getString(R.string.reply_label);
-
-RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
-        .setLabel(replyLabel)
-        .build();
-</pre>
-
-
-<h3>Add Pre-defined Text Responses</h3>
-
-<img src="{@docRoot}wear/images/12_voicereply.png" height="200" style="float:right;margin:0 0 20px 40px" />
-
-<p>In addition to allowing voice input, you can
-    provide up to five text responses that the user can select for quick replies. Call
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])"><code>setChoices()</code></a> and pass it a string array.</p>
-
-<p>For example, you may define some responses in a resource array:</p>
-
-<p class="code-caption">res/values/strings.xml</code>
-<pre class="prettyprint">
-&lt;?xml version="1.0" encoding="utf-8"?>
-&lt;resources>
-    &lt;string-array name="reply_choices">
-        &lt;item>Yes&lt;/item>
-        &lt;item>No&lt;/item>
-        &lt;item>Maybe&lt;/item>
-    &lt;/string-array>
-&lt;/resources>
-</pre>
-
-<p>Then, inflate the string array and add it to the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html"><code>RemoteInput</code></a>:</p>
-
-<pre>
-String replyLabel = getResources().getString(R.string.reply_label);
-String[] replyChoices = getResources().getStringArray(R.array.reply_choices);
-
-RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
-        .setLabel(replyLabel)
-        .setChoices(replyChoices)
-        .build();
-</pre>
-
-
-
-
-<h2 id="PrimaryAction">Receive Voice Input for the Primary Action</h2>
-
-<p>If "Reply" is your notification's primary action (defined by the {@link
-android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()}
-method), then you should attach the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html"><code>RemoteInput</code></a> to the main action using
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#addRemoteInputForContentIntent(android.support.wearable.notifications.RemoteInput)">
-<code>addRemoteInputForContentIntent()</code></a>. For example:</p>
-
-<pre>
-// Create intent for reply action
-Intent replyIntent = new Intent(this, ReplyActivity.class);
-PendingIntent replyPendingIntent =
-        PendingIntent.getActivity(this, 0, replyIntent, 0);
-
-// Build the notification
-NotificationCompat.Builder replyNotificationBuilder =
-        new NotificationCompat.Builder(this)
-        .setSmallIcon(R.drawable.ic_new_message)
-        .setContentTitle("Message from Travis")
-        .setContentText("I love key lime pie!")
-        .setContentIntent(replyPendingIntent);
-
-// Create the remote input
-RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
-        .setLabel(replyLabel)
-        .build();
-
-// Add remote input to wearable options and apply to notification
-Notification replyNotification =
-        new WearableNotificationOptions.Builder()
-        .addRemoteInputForContentIntent(remoteInput)
-        .build()
-        .applyTo(replyNotificationBuilder)
-        .build();
-</pre>
-
-<p>By using
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#addRemoteInputForContentIntent(android.support.wearable.notifications.RemoteInput)">
-<code>addRemoteInputForContentIntent()</code></a> to add the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html"><code>RemoteInput</code></a> object to the notification's primary action,
-the button that normally appears as an "Open" action becomes the "Reply" action
-and starts the voice input UI when users select it on Android Wear.</p>
-
-
-
-<h2 id="NewAction">Receive Voice Input for a Secondary Action</h2>
-
-<p>If the "Reply" action is not your notification's primary action and you want to enable
-voice input for a secondary action, add the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html"><code>RemoteInput</code></a> to a new action button defined by an
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableAction.html">
-<code>Action</code></a> object.</p>
-
-<p>You should instantiate the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableAction.html">
-<code>WearableAction</code></a> with the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableAction.Builder.html"><code>WearableAction.Builder()</code></a>
-constructor, which takes an icon and text label for the action button, plus the
-{@link android.app.PendingIntent}
-the system should use to invoke your app when the user selects the action. For example:</p>
-
-<pre>
-// Create the pending intent to fire when the user selects the action
-Intent replyIntent = new Intent(this, ReplyActivity.class);
-PendingIntent pendingReplyIntent =
-        PendingIntent.getActivity(this, 0, replyIntent, 0);
-
-// Create the remote input
-RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
-        .setLabel(replyLabel)
-        .build();
-
-// Create the notification action
-WearableAction replyAction = new WearableAction.Builder(R.drawable.ic_message,
-        "Reply", pendingIntent)
-        .addRemoteInput(remoteInput)
-        .build();
-</pre>
-
-
-<p>After you add the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html"><code>RemoteInput</code></a> to the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableAction.html">
-<code>Wearablection</code></a>, set the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableAction.html">
-<code>WearableAction</code></a> on the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html"><code>WearableNotifications.Builder</code></a> using
-  <a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationsOptions.Builder.html#addAction(Action)"><code>addAction()</code></a>.
-For example:</p>
-
-<pre>
-// Create basic notification builder
-NotificationCompat.Builder replyNotificationBuilder =
-        new NotificationCompat.Builder(this)
-                .setContentTitle("New message");
-
-// Create the notification action and add remote input
-WearableAction replyAction = new WearableAction.Builder(R.drawable.ic_message,
-        "Reply", pendingIntent)
-        .addRemoteInput(remoteInput)
-        .build();
-
-// Create wearable notification and add action
-Notification replyNotification =
-        new WearableNotificationOptions.Builder()
-                .addAction(replyAction)
-                .build()
-                .applyTo(replyNotificationBuilder)
-                .build();
-</pre>
-
-
-<p>Now, when the user selects "Reply" from an Android wearable, the system prompts the user
-    for voice input (and shows the list of pre-defined replies, if provided).
-    Once the user completes a response, the system invokes
-    the {@link android.content.Intent} attached to the action and adds the
-<code>EXTRA_VOICE_REPLY</code> extra (the string
-    you passed to the
-  <a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> constructor)
-  with the user's message as the string value.</p>
-
-<h2 id="ObtainInput">Obtaining the Voice Input as a String</h2>
-<p>To obtain the user's voice input, call
-<a href="{@docRoot}reference/android/support/wearable/notifications/RemoteInput.html#getResultsFromIntent(Intent)"><code>getResultsFromIntent()</code></a>,
-passing in the "Reply" action's intent. This method returns
-a {@link android.os.Bundle} that represents the intent's extras. You can then query the
-{@link android.os.Bundle} to obtain the user's voice input string.
-</p>
-<p>
-The following code shows a method that accepts an intent and returns the voice input string,
-which is referenced by the <code>EXTRA_VOICE_REPLY</code> key that is used in the previous examples:
-</p>
-<pre>
-/**
- * Obtain the intent that started this activity by calling
- * Activity.getIntent() and pass it into this method to
- * get the associated voice input string.
- */
-private String getMessageText(Intent intent) {
-    Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
-        if (remoteInput != null) {
-            return remoteInput.getString(Intent.EXTRA_VOICE_REPLY);
-        }
-    }
-    return null;
-}
-</pre>
-
-</body>
-</html>
diff --git a/docs/html/wear/notifications/stacks.jd b/docs/html/wear/notifications/stacks.jd
deleted file mode 100644
index 3c3dc09..0000000
--- a/docs/html/wear/notifications/stacks.jd
+++ /dev/null
@@ -1,138 +0,0 @@
-page.title=Stacking Notifications
-
-@jd:body
-
-<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
-<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
-
-<p>When creating notifications for a handheld device, you should always aggregate similar
-notifications into a single summary notification. For example, if your app creates notifications
-for received messages, you should not show more than one notification
-on a handheld device&mdash;when more than one is message is received, use a single notification
-to provide a summary such as "2 new messages."</p>
-
-<p>However, a summary notification is less useful on an Android wearable because users
-are not able to read details from each message on the wearable (they must open your app on the
-handheld to view more information). So for the wearable device, you should
-group all the notifications together in a stack. The stack of notifications appears as a single
-card, which users can expand to view the details from each notification separately. The new
-<a href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#setGroup(java.lang.String, int)">
-<code>setGroup()</code></a> method makes this possible while allowing you to still provide
-only one summary notification on the handheld device.</p>
-
-<p>For details about designing notification stacks, see the
-<a href="{@docRoot}wear/design/index.html#NotificationStacks">Design Principles of Android
-Wear</a>.</p>
-
-
-<h2 id="AddGroup">Add Each Notification to a Group</h2>
-
-<p>To create a stack, call <a
-href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#setGroup(java.lang.String, int)">
-<code>setGroup()</code></a> for each notification you want in the stack and specify a
-group key. Then call <a href="{@docRoot}reference/android/support/wearable/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a> to send it to the wearable.</p>
-
-<pre style="clear:right">
-final static String GROUP_KEY_EMAILS = "group_key_emails";
-
-// Build the notification
-NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle("New mail from " + sender1)
-         .setContentText(subject1)
-         .setSmallIcon(R.drawable.new_mail);
-
-// Set the group with WearableNotificationOptions.Builder and apply to the notification
-Notification notif1 = new WearableNotificationOptions.Builder()
-         .setGroup(GROUP_KEY_EMAILS)
-         .build()
-         .applyTo(builder)
-         .build();
-
-// Issue the notification
-NotificationManagerCompat notificationManager =
-        NotificationManagerCompat.from(this);
-notificationManager.notify(notificationId1, notif);
-</pre>
-
-<p>Later on, when you create another notification, specify
-the same group key. When you call
-<a href="{@docRoot}reference/android/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a>,
-this notification appears in the same stack as the previous notification,
-instead of as a new card:</p>
-
-<pre style="clear:right">
-builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle("New mail from " + sender2)
-         .setContentText(subject2)
-         .setSmallIcon(R.drawable.new_mail);
-
-// Use the same group as the previous notification
-Notification notif2 = new WearableNotificationOptions.Builder()
-         .setGroup(GROUP_KEY_EMAILS)
-         .build()
-         .applyTo(builder)
-         .build();
-
-notificationManager.notify(notificationId2, notif);
-</pre>
-
-<p>By default, notifications appear in the order in which you added them, with the most recent
-  notification visible at the top.  You can define a specific position in the group
-  by passing an order position as the second parameter for <a
-href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationOptions.Builder.html#setGroup(java.lang.String, int)">
-<code>setGroup()</code></a>.</p>
-
-
-<h2 id="AddSummary">Add a Summary Notification</h2>
-
-<img src="{@docRoot}wear/images/notif_summary_framed.png" height="242" width="330" style="float:right;margin:0 0 20px 40px" alt="" />
-
-<p>It's important that you still provide a summary notification that appears on handheld devices.
-So in addition to adding each unique notification to the same stack group, also add a summary
-notification, but set its order position to be <a
-href="{@docRoot}reference/android/support/wearable/notifications/WearableNotificationsOptions.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
-
-<p>This notification does not appear in your stack of notifications on the wearable, but
-appears as the only notification on the handheld device.</p>
-
-<pre style="clear:right">
-Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
-        R.drawable.ic_large_icon);
-
-// Create an InboxStyle notification
-builder = new NotificationCompat.Builder(this)
-        .setContentTitle("2 new messages")
-        .setSmallIcon(R.drawable.ic_small_icon)
-        .setLargeIcon(largeIcon)
-        .setStyle(new NotificationCompat.InboxStyle()
-                .addLine("Alex Faaborg   Check this out")
-                .addLine("Jeff Chang   Launch Party")
-                .setBigContentTitle("2 new messages")
-                .setSummaryText("johndoe@gmail.com"));
-
-// Specify the notification to be the group summary
-Notification summaryNotification = new WearableNotificationOptions.Builder()
-        .setGroupSummary(GROUP_KEY_EMAILS)
-        .build()
-        .applyTo(builder)
-        .build();
-
-notificationManager.notify(notificationId3, summaryNotification);
-</pre>
-
-<p>
-This notification uses {@link android.support.v4.app.NotificationCompat.InboxStyle},
-which gives you an easy way to create notifications for email or messaging apps.
-You can use this style, another one defined in {@link android.support.v4.app.NotificationCompat},
-or no style for the summary notification.
-</p>
-
-<p class="note"><b>Tip:</b>
-To style the text like in the example screenshot, see
-<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithHTML">Styling
-with HTML markup</a> and
-<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithSpannables">Styling
-with Spannables</a>.
-</p>
-</body>
-</html>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 38b8aaf..ed44cde 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -16,12 +16,14 @@
 
 package android.graphics.drawable;
 
+import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Insets;
+import android.graphics.Outline;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.PorterDuff.Mode;
@@ -123,6 +125,14 @@
     }
 
     @Override
+    public boolean getOutline(@NonNull Outline outline) {
+        if (mCurrDrawable != null) {
+            return mCurrDrawable.getOutline(outline);
+        }
+        return false;
+    }
+
+    @Override
     public void setAlpha(int alpha) {
         if (!mHasAlpha || mAlpha != alpha) {
             mHasAlpha = true;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 5cea7c9..2f22392 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -16,12 +16,14 @@
 
 package android.graphics.drawable;
 
+import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Outline;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
@@ -563,6 +565,16 @@
         }
     }
 
+    /**
+     * Builds an Outline from the first child Drawable, if present.
+     */
+    @Override
+    public boolean getOutline(@NonNull Outline outline) {
+        if (mLayerState.mNum < 1) return false;
+        final Drawable firstChild = mLayerState.mChildren[0].mDrawable;
+        return firstChild.getOutline(outline);
+    }
+
     @Override
     public void setHotspot(float x, float y) {
         final ChildDrawable[] array = mLayerState.mChildren;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index fb19242..c8b51e0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2811,38 +2811,6 @@
      * Only useful for volume controllers.
      * @hide
      */
-    public int getRemoteStreamVolume() {
-        // TODO STOPSHIP switch callers to use media sessions instead
-        Log.e(TAG, "Need to implement new Remote Volume!");
-        return 0;
-    }
-
-    /**
-     * Only useful for volume controllers.
-     * @hide
-     */
-    public int getRemoteStreamMaxVolume() {
-        // TODO STOPSHIP switch callers to use media sessions instead
-        Log.e(TAG, "Need to implement new Remote Volume!");
-        return 0;
-    }
-
-    /**
-     * Only useful for volume controllers.
-     * @hide
-     */
-    public void setRemoteStreamVolume(int index) {
-        try {
-            getService().setRemoteStreamVolume(index);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error setting remote stream volume", e);
-        }
-    }
-
-    /**
-     * Only useful for volume controllers.
-     * @hide
-     */
     public boolean isStreamAffectedByRingerMode(int streamType) {
         try {
             return getService().isStreamAffectedByRingerMode(streamType);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 0c224a6..8dd2721 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -85,6 +85,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -849,10 +850,8 @@
         }
 
         if (streamType == STREAM_REMOTE_MUSIC) {
-            // don't play sounds for remote
-            flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME);
-            //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
-            mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
+            // TODO bounce it to MediaSessionService to find an appropriate
+            // session
         } else {
             adjustStreamVolume(streamType, direction, flags, callingPackage);
         }
@@ -4794,4 +4793,91 @@
         }
         mVolumeController.setController(controller);
     }
+
+    public static class VolumeController {
+        private static final String TAG = "VolumeController";
+
+        private IVolumeController mController;
+
+        public void setController(IVolumeController controller) {
+            mController = controller;
+        }
+
+        public boolean isSameBinder(IVolumeController controller) {
+            return Objects.equals(asBinder(), binder(controller));
+        }
+
+        public IBinder asBinder() {
+            return binder(mController);
+        }
+
+        private static IBinder binder(IVolumeController controller) {
+            return controller == null ? null : controller.asBinder();
+        }
+
+        @Override
+        public String toString() {
+            return "VolumeController(" + asBinder() + ")";
+        }
+
+        public void postDisplaySafeVolumeWarning(int flags) {
+            if (mController == null)
+                return;
+            try {
+                mController.displaySafeVolumeWarning(flags);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling displaySafeVolumeWarning", e);
+            }
+        }
+
+        public void postVolumeChanged(int streamType, int flags) {
+            if (mController == null)
+                return;
+            try {
+                mController.volumeChanged(streamType, flags);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling volumeChanged", e);
+            }
+        }
+
+        public void postMasterVolumeChanged(int flags) {
+            if (mController == null)
+                return;
+            try {
+                mController.masterVolumeChanged(flags);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling masterVolumeChanged", e);
+            }
+        }
+
+        public void postMasterMuteChanged(int flags) {
+            if (mController == null)
+                return;
+            try {
+                mController.masterMuteChanged(flags);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling masterMuteChanged", e);
+            }
+        }
+
+        public void setLayoutDirection(int layoutDirection) {
+            if (mController == null)
+                return;
+            try {
+                mController.setLayoutDirection(layoutDirection);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling setLayoutDirection", e);
+            }
+        }
+
+        public void postDismiss() {
+            if (mController == null)
+                return;
+            try {
+                mController.dismiss();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling dismiss", e);
+            }
+        }
+    }
 }
diff --git a/media/java/android/media/IRemoteVolumeController.aidl b/media/java/android/media/IRemoteVolumeController.aidl
new file mode 100644
index 0000000..e4a4a42
--- /dev/null
+++ b/media/java/android/media/IRemoteVolumeController.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package android.media;
+
+import android.media.session.ISessionController;
+
+/**
+ * AIDL for the MediaSessionService to report interesting events on remote playback
+ * to a volume control dialog. See also IVolumeController for the AudioService half.
+ * TODO add in better support for multiple remote sessions.
+ * @hide
+ */
+oneway interface IRemoteVolumeController {
+    void remoteVolumeChanged(ISessionController session, int flags);
+    // sets the default session to use with the slider, replaces remoteSliderVisibility
+    // on IVolumeController
+    void updateRemoteController(ISessionController session);
+}
diff --git a/media/java/android/media/IVolumeController.aidl b/media/java/android/media/IVolumeController.aidl
index 35d7708..e3593a6 100644
--- a/media/java/android/media/IVolumeController.aidl
+++ b/media/java/android/media/IVolumeController.aidl
@@ -16,17 +16,12 @@
 
 package android.media;
 
-
 /**
- * AIDL for the AudioService to report interesting events to a remote volume control dialog.
+ * AIDL for the AudioService to report interesting events to a volume control
+ * dialog in another process.
  * @hide
  */
 oneway interface IVolumeController {
-    void hasNewRemotePlaybackInfo();
-
-    void remoteVolumeChanged(int streamType, int flags);
-
-    void remoteSliderVisibility(boolean visible);
 
     void displaySafeVolumeWarning(int flags);
 
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index a4a7c4e..6004ecf 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -77,7 +77,7 @@
     private final MediaEventHandler mEventHandler;
     private final Context mContext;
     private final ContentResolver mContentResolver;
-    private final VolumeController mVolumeController;
+    private final AudioService.VolumeController mVolumeController;
     private final BroadcastReceiver mReceiver = new PackageIntentsReceiver();
     private final AppOpsManager mAppOps;
     private final KeyguardManager mKeyguardManager;
@@ -85,7 +85,7 @@
     private final NotificationListenerObserver mNotifListenerObserver;
 
     protected MediaFocusControl(Looper looper, Context cntxt,
-            VolumeController volumeCtrl, AudioService as) {
+            AudioService.VolumeController volumeCtrl, AudioService as) {
         mEventHandler = new MediaEventHandler(looper);
         mContext = cntxt;
         mContentResolver = mContext.getContentResolver();
@@ -2061,29 +2061,6 @@
         }
     }
 
-    protected void adjustRemoteVolume(int streamType, int direction, int flags) {
-        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
-        boolean volFixed = false;
-        synchronized (mMainRemote) {
-            if (!mMainRemoteIsActive) {
-                if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
-                return;
-            }
-            rccId = mMainRemote.mRccId;
-            volFixed = (mMainRemote.mVolumeHandling ==
-                    RemoteControlClient.PLAYBACK_VOLUME_FIXED);
-        }
-        // unlike "local" stream volumes, we can't compute the new volume based on the direction,
-        // we can only notify the remote that volume needs to be updated, and we'll get an async'
-        // update through setPlaybackInfoForRcc()
-        if (!volFixed) {
-            sendVolumeUpdateToRemote(rccId, direction);
-        }
-
-        // fire up the UI
-        mVolumeController.postRemoteVolumeChanged(streamType, flags);
-    }
-
     private void sendVolumeUpdateToRemote(int rccId, int direction) {
         if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
         if (direction == 0) {
@@ -2183,27 +2160,9 @@
     }
 
     private void onReevaluateRemote() {
-        if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
-        // is there a registered RemoteControlClient that is handling remote playback
-        boolean hasRemotePlayback = false;
-        synchronized (mPRStack) {
-            // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
-            //   traversal order doesn't matter
-            Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
-            while(stackIterator.hasNext()) {
-                PlayerRecord prse = stackIterator.next();
-                if (prse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
-                    hasRemotePlayback = true;
-                    break;
-                }
-            }
-        }
-        synchronized (mMainRemote) {
-            if (mHasRemotePlayback != hasRemotePlayback) {
-                mHasRemotePlayback = hasRemotePlayback;
-                mVolumeController.postRemoteSliderVisibility(hasRemotePlayback);
-            }
-        }
+        // TODO This was used to notify VolumePanel if there was remote playback
+        // in the stack. This is now in MediaSessionService. More code should be
+        // removed.
     }
 
 }
diff --git a/media/java/android/media/VolumeController.java b/media/java/android/media/VolumeController.java
deleted file mode 100644
index 6b70cc3..0000000
--- a/media/java/android/media/VolumeController.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * 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.
- */
-
-package android.media;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Objects;
-
-/**
- * Wraps the remote volume controller interface as a convenience to audio service.
- * @hide
- */
-public class VolumeController {
-    private static final String TAG = "VolumeController";
-
-    private IVolumeController mController;
-
-    public void setController(IVolumeController controller) {
-        mController = controller;
-    }
-
-    public boolean isSameBinder(IVolumeController controller) {
-        return Objects.equals(asBinder(), binder(controller));
-    }
-
-    public IBinder asBinder() {
-        return binder(mController);
-    }
-
-    private static IBinder binder(IVolumeController controller) {
-        return controller == null ? null : controller.asBinder();
-    }
-
-    @Override
-    public String toString() {
-        return "VolumeController(" + asBinder() + ")";
-    }
-
-    public void postHasNewRemotePlaybackInfo() {
-        if (mController == null) return;
-        try {
-            mController.hasNewRemotePlaybackInfo();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling hasNewRemotePlaybackInfo", e);
-        }
-    }
-
-    public void postRemoteVolumeChanged(int streamType, int flags) {
-        if (mController == null) return;
-        try {
-            mController.remoteVolumeChanged(streamType, flags);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling remoteVolumeChanged", e);
-        }
-    }
-
-    public void postRemoteSliderVisibility(boolean visible) {
-        if (mController == null) return;
-        try {
-            mController.remoteSliderVisibility(visible);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling remoteSliderVisibility", e);
-        }
-    }
-
-    public void postDisplaySafeVolumeWarning(int flags) {
-        if (mController == null) return;
-        try {
-            mController.displaySafeVolumeWarning(flags);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling displaySafeVolumeWarning", e);
-        }
-    }
-
-    public void postVolumeChanged(int streamType, int flags) {
-        if (mController == null) return;
-        try {
-            mController.volumeChanged(streamType, flags);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling volumeChanged", e);
-        }
-    }
-
-    public void postMasterVolumeChanged(int flags) {
-        if (mController == null) return;
-        try {
-            mController.masterVolumeChanged(flags);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling masterVolumeChanged", e);
-        }
-    }
-
-    public void postMasterMuteChanged(int flags) {
-        if (mController == null) return;
-        try {
-            mController.masterMuteChanged(flags);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling masterMuteChanged", e);
-        }
-    }
-
-    public void setLayoutDirection(int layoutDirection) {
-        if (mController == null) return;
-        try {
-            mController.setLayoutDirection(layoutDirection);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling setLayoutDirection", e);
-        }
-    }
-
-    public void postDismiss() {
-        if (mController == null) return;
-        try {
-            mController.dismiss();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error calling dismiss", e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index e823153..baa1379 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -17,6 +17,7 @@
 
 import android.media.MediaMetadata;
 import android.media.session.RouteInfo;
+import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 
@@ -30,4 +31,5 @@
     // These callbacks are for the TransportController
     void onPlaybackStateChanged(in PlaybackState state);
     void onMetadataChanged(in MediaMetadata metadata);
+    void onVolumeInfoChanged(in ParcelableVolumeInfo info);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index bd1fa85..dce84d4 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -16,6 +16,7 @@
 package android.media.session;
 
 import android.content.ComponentName;
+import android.media.IRemoteVolumeController;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
@@ -34,4 +35,7 @@
     void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
             int userId);
     void removeSessionsListener(in IActiveSessionsListener listener);
+
+    // This is for the system volume UI only
+    void setRemoteVolumeController(in IRemoteVolumeController rvc);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index cc3db26..7653e5a 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -52,6 +53,7 @@
     private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
     private static final int MSG_UPDATE_METADATA = 3;
     private static final int MSG_ROUTE = 4;
+    private static final int MSG_UPDATE_VOLUME = 5;
 
     private final ISessionController mSessionBinder;
 
@@ -203,6 +205,43 @@
     }
 
     /**
+     * Set the volume of the stream or output this session is playing on. The
+     * command will be ignored if it does not support
+     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
+     * {@link AudioManager} may be used to affect the handling.
+     *
+     * @see #getVolumeInfo()
+     * @param value The value to set it to, between 0 and the reported max.
+     * @param flags Any flags to pass with the command.
+     */
+    public void setVolumeTo(int value, int flags) {
+        try {
+            mSessionBinder.setVolumeTo(value, flags);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling setVolumeTo.", e);
+        }
+    }
+
+    /**
+     * Adjust the volume of the stream or output this session is playing on.
+     * Negative values will lower the volume. The command will be ignored if it
+     * does not support {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
+     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
+     * {@link AudioManager} may be used to affect the handling.
+     *
+     * @see #getVolumeInfo()
+     * @param delta The number of steps to adjust the volume by.
+     * @param flags Any flags to pass with the command.
+     */
+    public void adjustVolumeBy(int delta, int flags) {
+        try {
+            mSessionBinder.adjustVolumeBy(delta, flags);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
+        }
+    }
+
+    /**
      * Adds a callback to receive updates from the Session. Updates will be
      * posted on the caller's thread.
      *
@@ -406,6 +445,14 @@
          */
         public void onMetadataChanged(@Nullable MediaMetadata metadata) {
         }
+
+        /**
+         * Override to handle changes to the volume info.
+         *
+         * @param info The current volume info for this session.
+         */
+        public void onVolumeInfoChanged(VolumeInfo info) {
+        }
     }
 
     /**
@@ -552,8 +599,8 @@
         /**
          * Get the type of volume handling, either local or remote. One of:
          * <ul>
-         * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li>
-         * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li>
+         * <li>{@link MediaSession#PLAYBACK_TYPE_LOCAL}</li>
+         * <li>{@link MediaSession#PLAYBACK_TYPE_REMOTE}</li>
          * </ul>
          *
          * @return The type of volume handling this session is using.
@@ -564,7 +611,7 @@
 
         /**
          * Get the stream this is currently controlling volume on. When the volume
-         * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not
+         * type is {@link MediaSession#PLAYBACK_TYPE_REMOTE} this value does not
          * have meaning and should be ignored.
          *
          * @return The stream this session is playing on.
@@ -646,6 +693,16 @@
             }
         }
 
+        @Override
+        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioStream, pvi.controlType,
+                        pvi.maxVolume, pvi.currentVolume);
+                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
+            }
+        }
+
     }
 
     private final static class MessageHandler extends Handler {
@@ -671,6 +728,9 @@
                 case MSG_UPDATE_METADATA:
                     mCallback.onMetadataChanged((MediaMetadata) msg.obj);
                     break;
+                case MSG_UPDATE_VOLUME:
+                    mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj);
+                    break;
             }
         }
 
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 8909d55..7637ec8 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -127,12 +127,12 @@
     /**
      * The session uses local playback.
      */
-    public static final int VOLUME_TYPE_LOCAL = 1;
+    public static final int PLAYBACK_TYPE_LOCAL = 1;
 
     /**
      * The session uses remote playback.
      */
-    public static final int VOLUME_TYPE_REMOTE = 2;
+    public static final int PLAYBACK_TYPE_REMOTE = 2;
 
     private final Object mLock = new Object();
 
@@ -265,7 +265,7 @@
      */
     public void setPlaybackToLocal(int stream) {
         try {
-            mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0);
+            mBinder.configureVolumeHandling(PLAYBACK_TYPE_LOCAL, stream, 0);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
         }
@@ -294,7 +294,7 @@
         });
 
         try {
-            mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+            mBinder.configureVolumeHandling(PLAYBACK_TYPE_REMOTE, volumeProvider.getVolumeControl(),
                     volumeProvider.getMaxVolume());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 882453f..9291bb0 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.media.IRemoteVolumeController;
 import android.media.session.ISessionManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -215,6 +216,21 @@
     }
 
     /**
+     * Set the remote volume controller to receive volume updates on. Only for
+     * use by system UI.
+     *
+     * @param rvc The volume controller to receive updates on.
+     * @hide
+     */
+    public void setRemoteVolumeController(IRemoteVolumeController rvc) {
+        try {
+            mService.setRemoteVolumeController(rvc);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setRemoteVolumeController.", e);
+        }
+    }
+
+    /**
      * Send a media key event. The receiver will be selected automatically.
      *
      * @param keyEvent The KeyEvent to send.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e9cb197..0df6c74 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -300,5 +300,11 @@
                 <category android:name="android.intent.category.DESK_DOCK" />
             </intent-filter>
         </activity>
+
+
+        <!-- I dream of notifications -->
+        <service
+            android:name=".doze.DozeService"
+            android:exported="true" />
     </application>
 </manifest>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 36c1994..370ff1c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -97,34 +97,6 @@
     <!-- half the distance between notifications in the panel -->
     <dimen name="notification_divider_height">2dp</dimen>
 
-    <!-- Notification drawer tuning parameters (phone UI) -->
-    <!-- Initial velocity of the shade when expanding on its own -->
-    <dimen name="self_expand_velocity">2000dp</dimen>
-    <!-- Initial velocity of the shade when collapsing on its own -->
-    <dimen name="self_collapse_velocity">2000dp</dimen>
-    <!-- Minimum final velocity of gestures interpreted as expand requests -->
-    <dimen name="fling_expand_min_velocity">100dp</dimen>
-    <!-- Minimum final velocity of gestures interpreted as collapse requests -->
-    <dimen name="fling_collapse_min_velocity">100dp</dimen>
-    <!-- Cap on contribution of x dimension of gesture to overall velocity -->
-    <dimen name="fling_gesture_max_x_velocity">200dp</dimen>
-    <!-- Cap on overall resulting fling speed (s^-1) -->
-    <dimen name="fling_gesture_max_output_velocity">3000dp</dimen>
-
-    <!-- Minimum distance a fling must travel (anti-jitter) -->
-    <dimen name="fling_gesture_min_dist">20dp</dimen>
-
-    <!-- Minimum fraction of the display a gesture must travel, at any velocity, to qualify as a
-         collapse request -->
-    <item type="dimen" name="collapse_min_display_fraction">10%</item>
-    <!-- Minimum fraction of the display a gesture must travel to qualify as an expand request -->
-    <item type="dimen" name="expand_min_display_fraction">50%</item>
-
-    <!-- Initial acceleration of an expand animation after fling -->
-    <dimen name="expand_accel">2000dp</dimen>
-    <!-- Initial acceleration of an collapse animation after fling -->
-    <dimen name="collapse_accel">2000dp</dimen>
-
     <!-- The padding on the global screenshot background image -->
     <dimen name="global_screenshot_bg_padding">20dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
new file mode 100644
index 0000000..cc0d4a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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
+ */
+
+package com.android.systemui.doze;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.os.PowerManager;
+import android.os.Vibrator;
+import android.service.dreams.DozeHardware;
+import android.service.dreams.DreamService;
+import android.util.Log;
+
+import com.android.systemui.SystemUIApplication;
+
+public class DozeService extends DreamService {
+    private static final boolean DEBUG = false;
+
+    private static final String TEASE_ACTION = "com.android.systemui.doze.tease";
+
+    private final String mTag = String.format("DozeService.%08x", hashCode());
+    private final Context mContext = this;
+
+    private Host mHost;
+    private DozeHardware mDozeHardware;
+    private SensorManager mSensors;
+    private Sensor mSigMotionSensor;
+    private PowerManager mPowerManager;
+    private PowerManager.WakeLock mWakeLock;
+    private boolean mDreaming;
+    private boolean mTeaseReceiverRegistered;
+
+    public DozeService() {
+        if (DEBUG) Log.d(mTag, "new DozeService()");
+        setDebug(DEBUG);
+    }
+
+    @Override
+    public void onCreate() {
+        if (DEBUG) Log.d(mTag, "onCreate");
+        super.onCreate();
+
+        if (getApplication() instanceof SystemUIApplication) {
+            final SystemUIApplication app = (SystemUIApplication) getApplication();
+            mHost = app.getComponent(Host.class);
+        }
+
+        setWindowless(true);
+
+        mSensors = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        if (DEBUG) Log.d(mTag, "onAttachedToWindow");
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    public void onDreamingStarted() {
+        super.onDreamingStarted();
+        mDozeHardware = getDozeHardware();
+        if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze()
+                + " dozeHardware=" + mDozeHardware);
+        mDreaming = true;
+        listenForTeaseSignals(true);
+        requestDoze();
+    }
+
+    public void stayAwake(long millis) {
+        if (mDreaming && millis > 0) {
+            mWakeLock.acquire(millis);
+        }
+    }
+
+    public void startDozing() {
+        if (DEBUG) Log.d(mTag, "startDozing mDreaming=" + mDreaming);
+        if (!mDreaming) {
+            Log.w(mTag, "Not dozing, no longer dreaming");
+            return;
+        }
+
+        super.startDozing();
+    }
+
+    @Override
+    public void onDreamingStopped() {
+        if (DEBUG) Log.d(mTag, "onDreamingStopped isDozing=" + isDozing());
+        super.onDreamingStopped();
+
+        mDreaming = false;
+        mDozeHardware = null;
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+        listenForTeaseSignals(false);
+        stopDozing();
+        dozingStopped();
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
+        super.onDetachedFromWindow();
+
+        dozingStopped();
+    }
+
+    @Override
+    public void onDestroy() {
+        if (DEBUG) Log.d(mTag, "onDestroy");
+        super.onDestroy();
+
+        dozingStopped();
+    }
+
+    private void requestDoze() {
+        if (mHost != null) {
+            mHost.requestDoze(this);
+        }
+    }
+
+    private void requestTease() {
+        if (mHost != null) {
+            mHost.requestTease(this);
+        }
+    }
+
+    private void dozingStopped() {
+        if (mHost != null) {
+            mHost.dozingStopped(this);
+        }
+    }
+
+    private void listenForTeaseSignals(boolean listen) {
+        if (DEBUG) Log.d(mTag, "listenForTeaseSignals: " + listen);
+        if (mHost == null) return;
+        listenForSignificantMotion(listen);
+        if (listen) {
+            mContext.registerReceiver(mTeaseReceiver, new IntentFilter(TEASE_ACTION));
+            mTeaseReceiverRegistered = true;
+            mHost.addCallback(mHostCallback);
+        } else {
+            if (mTeaseReceiverRegistered) {
+                mContext.unregisterReceiver(mTeaseReceiver);
+            }
+            mTeaseReceiverRegistered = false;
+            mHost.removeCallback(mHostCallback);
+        }
+    }
+
+    private void listenForSignificantMotion(boolean listen) {
+        if (mSigMotionSensor == null) return;
+        if (listen) {
+            mSensors.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
+        } else {
+            mSensors.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
+        }
+    }
+
+    private static String triggerEventToString(TriggerEvent event) {
+        if (event == null) return null;
+        final StringBuilder sb = new StringBuilder("TriggerEvent[")
+                .append(event.timestamp).append(',')
+                .append(event.sensor.getName());
+        if (event.values != null) {
+            for (int i = 0; i < event.values.length; i++) {
+                sb.append(',').append(event.values[i]);
+            }
+        }
+        return sb.append(']').toString();
+    }
+
+    private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
+        @Override
+        public void onTrigger(TriggerEvent event) {
+            if (DEBUG) Log.d(mTag, "sigMotion.onTrigger: " + triggerEventToString(event));
+            if (DEBUG) {
+                final Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+                if (v != null) {
+                    v.vibrate(1000);
+                }
+            }
+            requestTease();
+            listenForSignificantMotion(true);  // reregister, this sensor only fires once
+        }
+    };
+
+    private final BroadcastReceiver mTeaseReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Log.d(mTag, "Received tease intent");
+            requestTease();
+        }
+    };
+
+    private final Host.Callback mHostCallback = new Host.Callback() {
+        @Override
+        public void onNewNotifications() {
+            if (DEBUG) Log.d(mTag, "onNewNotifications");
+            requestTease();
+        }
+    };
+
+    public interface Host {
+        void addCallback(Callback callback);
+        void removeCallback(Callback callback);
+        void requestDoze(DozeService dozeService);
+        void requestTease(DozeService dozeService);
+        void dozingStopped(DozeService dozeService);
+
+        public interface Callback {
+            void onNewNotifications();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 2abbad5..8995437 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -35,8 +35,6 @@
             public static final boolean EnableTaskStackClipping = true;
             // Enables tapping on the TaskBar to launch the task
             public static final boolean EnableTaskBarTouchEvents = true;
-            // Enables the use of theme colors as the task bar background
-            public static final boolean EnableTaskBarThemeColors = true;
             // Enables app-info pane on long-pressing the icon
             public static final boolean EnableDevAppInfoOnLongPress = true;
             // Enables the search bar layout
@@ -95,6 +93,8 @@
         public static class App {
             public static int AppWidgetHostId = 1024;
             public static String Key_SearchAppWidgetId = "searchAppWidgetId";
+            public static String Key_DebugModeEnabled = "debugModeEnabled";
+            public static String DebugModeVersion = "A";
         }
         public static class Window {
             // The dark background dim is set behind the empty recents view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
new file mode 100644
index 0000000..d90e2be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.systemui.recents;
+
+import android.os.Handler;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+
+/**
+ * A trigger for catching a debug chord.
+ * We currently use volume up then volume down to trigger this mode.
+ */
+public class DebugTrigger {
+
+    Handler mHandler;
+    Runnable mTriggeredRunnable;
+
+    int mLastKeyCode;
+    long mLastKeyCodeTime;
+
+    public DebugTrigger(Runnable triggeredRunnable) {
+        mHandler = new Handler();
+        mTriggeredRunnable = triggeredRunnable;
+    }
+
+    /** Resets the debug trigger */
+    void reset() {
+        mLastKeyCode = 0;
+        mLastKeyCodeTime = 0;
+    }
+
+    /**
+     * Processes a key event and tests if it is a part of the trigger. If the chord is complete,
+     * then we just call the callback.
+     */
+    public void onKeyEvent(int keyCode) {
+        if (mLastKeyCode == 0) {
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+                mLastKeyCode = keyCode;
+                mLastKeyCodeTime = SystemClock.uptimeMillis();
+                return;
+            }
+        } else {
+            if (mLastKeyCode == KeyEvent.KEYCODE_VOLUME_UP &&
+                    keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+                if ((SystemClock.uptimeMillis() - mLastKeyCodeTime) < 750) {
+                    mTriggeredRunnable.run();
+                }
+            }
+        }
+        reset();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 52a17df..ecdc9dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.ActivityOptions;
+import android.app.SearchManager;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -25,13 +26,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewStub;
+import android.widget.Toast;
 import com.android.systemui.R;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.views.FullscreenTransitionOverlayView;
@@ -109,7 +113,7 @@
     }
 
     // Broadcast receiver to handle messages from our RecentsService
-    BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
+    final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -143,18 +147,29 @@
                 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
                 // Call our callback
                 onEnterAnimationTriggered();
+            } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
+                // Refresh the search widget
+                refreshSearchWidget();
             }
         }
     };
 
     // Broadcast receiver to handle messages from the system
-    BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+    final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             mFinishWithoutAnimationRunnable.run();
         }
     };
 
+    // Debug trigger
+    final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() {
+        @Override
+        public void run() {
+            onDebugModeTriggered();
+        }
+    });
+
     /** Updates the set of recent tasks */
     void updateRecentsTasks(Intent launchIntent) {
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -431,6 +446,7 @@
         filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
         filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
         filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
+        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register the broadcast receiver to handle messages when the screen is turned off
@@ -513,7 +529,8 @@
             mRecentsView.focusNextTask(!backward);
             return true;
         }
-
+        // Pass through the debug trigger
+        mDebugTrigger.onKeyEvent(keyCode);
         return super.onKeyDown(keyCode, event);
     }
 
@@ -546,6 +563,23 @@
         }
     }
 
+    /** Called when debug mode is triggered */
+    public void onDebugModeTriggered() {
+        if (mConfig.developerOptionsEnabled) {
+            SharedPreferences settings = getSharedPreferences(getPackageName(), 0);
+            if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) {
+                // Disable the debug mode
+                settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
+            } else {
+                // Enable the debug mode
+                settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
+            }
+            Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion +
+                    ") toggled, please restart Recents now", Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /** Called when the enter recents animation is triggered. */
     public void onEnterAnimationTriggered() {
         // Animate the scrims in
         mScrimViews.startEnterRecentsAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index d55eba7..3754340 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 
 /** Our special app widget host for the Search widget */
 public class RecentsAppWidgetHost extends AppWidgetHost {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 10978ca..d57f779 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -103,6 +103,7 @@
 
     /** Dev options */
     public boolean developerOptionsEnabled;
+    public boolean debugModeEnabled;
 
     /** Private constructor */
     private RecentsConfiguration(Context context) {
@@ -141,10 +142,14 @@
 
     /** Updates the state, given the specified context */
     void update(Context context) {
+        SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
         Resources res = context.getResources();
         DisplayMetrics dm = res.getDisplayMetrics();
         mDisplayMetrics = dm;
 
+        // Debug mode
+        debugModeEnabled = settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false);
+
         // Animations
         animationPxMovementPerSecond =
                 res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
@@ -168,7 +173,6 @@
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
 
         // Update the search widget id
-        SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
         searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
 
         // Task stack
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index e554af7..e40d732 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -26,6 +26,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.views.TaskStackView;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index 25875bc..6d6376f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -68,20 +68,20 @@
     }
 
     /** Calculates the luminance-preserved greyscale of a given color. */
-    private static int colorToGreyscale(int color) {
+    public static int colorToGreyscale(int color) {
         return Math.round(0.2126f * Color.red(color) + 0.7152f * Color.green(color) +
                 0.0722f * Color.blue(color));
     }
 
     /** Returns the ideal color to draw on top of a specified background color. */
-    public static int getIdealColorForBackgroundColor(int color, int lightRes, int darkRes) {
-        int greyscale = colorToGreyscale(color);
+    public static int getIdealColorForBackgroundColorGreyscale(int greyscale, int lightRes,
+                                                               int darkRes) {
         return (greyscale < 128) ? lightRes : darkRes;
     }
     /** Returns the ideal drawable to draw on top of a specified background color. */
-    public static Drawable getIdealResourceForBackgroundColor(int color, Drawable lightRes,
-                                                           Drawable darkRes) {
-        int greyscale = colorToGreyscale(color);
+    public static Drawable getIdealResourceForBackgroundColorGreyscale(int greyscale,
+                                                                       Drawable lightRes,
+                                                                       Drawable darkRes) {
         return (greyscale < 128) ? lightRes : darkRes;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
new file mode 100644
index 0000000..1344729
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.systemui.recents.model;
+
+import android.graphics.Bitmap;
+
+/**
+ * The Bitmap LRU cache.
+ */
+class BitmapLruCache extends KeyStoreLruCache<Bitmap> {
+    public BitmapLruCache(int cacheSize) {
+        super(cacheSize);
+    }
+
+    @Override
+    protected int computeSize(Bitmap b) {
+        // The cache size will be measured in kilobytes rather than number of items
+        return b.getAllocationByteCount() / 1024;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
new file mode 100644
index 0000000..61d19da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.systemui.recents.model;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * The Drawable LRU cache.
+ */
+class DrawableLruCache extends KeyStoreLruCache<Drawable> {
+    public DrawableLruCache(int cacheSize) {
+        super(cacheSize);
+    }
+
+    @Override
+    protected int computeSize(Drawable d) {
+        // The cache size will be measured in kilobytes rather than number of items
+        // NOTE: this isn't actually correct, as the icon may be smaller
+        int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
+        return maxBytes / 1024;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
new file mode 100644
index 0000000..3ccca9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.systemui.recents.model;
+
+import android.util.LruCache;
+
+import java.util.HashMap;
+
+/**
+ * An LRU cache that support querying the keys as well as values. By using the Task's key, we can
+ * prevent holding onto a reference to the Task resource data, while keeping the cache data in
+ * memory where necessary.
+ */
+public class KeyStoreLruCache<V> {
+    // We keep a set of keys that are associated with the LRU cache, so that we can find out
+    // information about the Task that was previously in the cache.
+    HashMap<Task.TaskKey, Task.TaskKey> mKeys = new HashMap<Task.TaskKey, Task.TaskKey>();
+    // The cache implementation
+    LruCache<Task.TaskKey, V> mCache;
+
+    public KeyStoreLruCache(int cacheSize) {
+        mCache = new LruCache<Task.TaskKey, V>(cacheSize) {
+            @Override
+            protected int sizeOf(Task.TaskKey t, V v) {
+                return computeSize(v);
+            }
+
+            @Override
+            protected void entryRemoved(boolean evicted, Task.TaskKey key, V oldV, V newV) {
+                mKeys.remove(key);
+            }
+        };
+    }
+
+    /** Computes the size of a value. */
+    protected int computeSize(V value) {
+        return 0;
+    }
+
+    /** Gets a specific entry in the cache. */
+    final V get(Task.TaskKey key) {
+        return mCache.get(key);
+    }
+
+    /**
+     * Returns the value only if the last active time of the key currently in the lru cache is
+     * greater than or equal to the last active time of the key specified.
+     */
+    final V getCheckLastActiveTime(Task.TaskKey key) {
+        Task.TaskKey lruKey = mKeys.get(key);
+        if (lruKey != null && (lruKey.lastActiveTime < key.lastActiveTime)) {
+            // The task has changed (been made active since the last time it was put into the
+            // LRU cache) so invalidate that item in the cache
+            remove(lruKey);
+            return null;
+        }
+        // Either the task does not exist in the cache, or the last active time is the same as
+        // the key specified
+        return mCache.get(key);
+    }
+
+    /** Gets the previous task key that matches the specified key. */
+    final Task.TaskKey getKey(Task.TaskKey key) {
+        return mKeys.get(key);
+    }
+
+    /** Puts an entry in the cache for a specific key. */
+    final void put(Task.TaskKey key, V value) {
+        mCache.put(key, value);
+        if (mKeys.containsKey(key)) {
+            mKeys.get(key).updateLastActiveTime(key.lastActiveTime);
+        } else {
+            mKeys.put(key, key);
+        }
+    }
+
+    /** Removes a cache entry for a specific key. */
+    final void remove(Task.TaskKey key) {
+        mCache.remove(key);
+        mKeys.remove(key);
+    }
+
+    /** Removes all the entries in the cache. */
+    final void evictAll() {
+        mCache.evictAll();
+        mKeys.clear();
+    }
+
+    /** Returns the size of the cache. */
+    final int size() {
+        return mCache.size();
+    }
+
+    /** Trims the cache to a specific size */
+    final void trimToSize(int cacheSize) {
+        mCache.resize(cacheSize);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
rename to packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 04d1f1f..cbe39e3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.model;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Looper;
 import com.android.internal.content.PackageMonitor;
+import com.android.systemui.recents.SystemServicesProxy;
 
 import java.util.HashSet;
 import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
rename to packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 07a6a56..99910c7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.model;
 
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
@@ -27,11 +27,11 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.UserHandle;
-import android.util.LruCache;
 import android.util.Pair;
-import com.android.systemui.recents.model.SpaceNode;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.SystemServicesProxy;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -204,8 +204,8 @@
                 final Task t = nextTaskData.first;
                 final boolean forceLoadTask = nextTaskData.second;
                 if (t != null) {
-                    Drawable loadIcon = mApplicationIconCache.get(t.key);
-                    Bitmap loadThumbnail = mThumbnailCache.get(t.key);
+                    Drawable loadIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
+                    Bitmap loadThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
                     if (Console.Enabled) {
                         Console.log(Constants.Log.App.TaskDataLoader,
                                 "  [TaskResourceLoader|load]",
@@ -255,7 +255,7 @@
                         mMainThreadHandler.post(new Runnable() {
                             @Override
                             public void run() {
-                                t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask);
+                                t.notifyTaskDataLoaded(newThumbnail, newIcon);
                             }
                         });
                     }
@@ -282,40 +282,6 @@
     }
 }
 
-/**
- * The drawable cache.  By using the Task's key, we can prevent holding onto a reference to the Task
- * resource data, while keeping the cache data in memory where necessary.
- */
-class DrawableLruCache extends LruCache<Task.TaskKey, Drawable> {
-    public DrawableLruCache(int cacheSize) {
-        super(cacheSize);
-    }
-
-    @Override
-    protected int sizeOf(Task.TaskKey t, Drawable d) {
-        // The cache size will be measured in kilobytes rather than number of items
-        // NOTE: this isn't actually correct, as the icon may be smaller
-        int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
-        return maxBytes / 1024;
-    }
-}
-
-/**
- * The bitmap cache.  By using the Task's key, we can prevent holding onto a reference to the Task
- * resource data, while keeping the cache data in memory where necessary.
- */
-class BitmapLruCache extends LruCache<Task.TaskKey, Bitmap> {
-    public BitmapLruCache(int cacheSize) {
-        super(cacheSize);
-    }
-
-    @Override
-    protected int sizeOf(Task.TaskKey t, Bitmap bitmap) {
-        // The cache size will be measured in kilobytes rather than number of items
-        return bitmap.getAllocationByteCount() / 1024;
-    }
-}
-
 /* Recents task loader
  * NOTE: We should not hold any references to a Context from a static instance */
 public class RecentsTaskLoader {
@@ -417,12 +383,13 @@
     }
 
     /** Reload the set of recent tasks */
-    SpaceNode reload(Context context, int preloadCount) {
+    public SpaceNode reload(Context context, int preloadCount) {
         long t1 = System.currentTimeMillis();
 
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
         }
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
         Resources res = context.getResources();
         ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
         TaskStack stack = new TaskStack(context);
@@ -444,7 +411,7 @@
             ActivityManager.TaskDescription av = t.taskDescription;
             String activityLabel = null;
             BitmapDrawable activityIcon = null;
-            int activityColor = 0;
+            int activityColor = config.taskBarViewDefaultBackgroundColor;
             if (av != null) {
                 activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info));
                 activityIcon = (av.getIcon() != null) ? new BitmapDrawable(res, av.getIcon()) : null;
@@ -456,7 +423,7 @@
 
             // Create a new task
             Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
-                    activityIcon, activityColor, t.userId);
+                    activityIcon, activityColor, t.userId, t.lastActiveTime);
 
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
@@ -466,45 +433,44 @@
                             "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
                 }
 
-                // Load the icon (if possible and not the foremost task, from the cache)
-                if (!isForemostTask) {
-                    task.applicationIcon = mApplicationIconCache.get(task.key);
-                    if (task.applicationIcon != null) {
-                        // Even though we get things from the cache, we should update them
-                        // if they've changed in the bg
-                        tasksToForceLoad.add(task);
-                    }
-                }
+                // Load the icon from the cache if possible
+                task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key);
                 if (task.applicationIcon == null) {
-                    task.applicationIcon = ssp.getActivityIcon(info, task.userId);
-                    if (task.applicationIcon != null) {
-                        mApplicationIconCache.put(task.key, task.applicationIcon);
+                    if (isForemostTask) {
+                        // We force loading the application icon for the foremost task
+                        task.applicationIcon = ssp.getActivityIcon(info, task.userId);
+                        if (task.applicationIcon != null) {
+                            mApplicationIconCache.put(task.key, task.applicationIcon);
+                        } else {
+                            task.applicationIcon = mDefaultApplicationIcon;
+                        }
                     } else {
-                        task.applicationIcon = mDefaultApplicationIcon;
+                        // Either the task has updated, or we haven't cached any information for the
+                        // task, so reload it
+                        tasksToForceLoad.add(task);
                     }
                 }
 
                 // Load the thumbnail (if possible and not the foremost task, from the cache)
-                if (!isForemostTask) {
-                    task.thumbnail = mThumbnailCache.get(task.key);
-                    if (task.thumbnail != null && !tasksToForceLoad.contains(task)) {
-                        // Even though we get things from the cache, we should update them if
-                        // they've changed in the bg
-                        tasksToForceLoad.add(task);
-                    }
-                }
+                task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key);
                 if (task.thumbnail == null) {
                     if (Console.Enabled) {
                         Console.log(Constants.Log.App.TaskDataLoader,
                                 "[RecentsTaskLoader|loadingTaskThumbnail]");
                     }
-
-                    task.thumbnail = ssp.getTaskThumbnail(task.key.id);
-                    if (task.thumbnail != null) {
-                        task.thumbnail.setHasAlpha(false);
-                        mThumbnailCache.put(task.key, task.thumbnail);
+                    if (isForemostTask) {
+                        // We force loading the thumbnail icon for the foremost task
+                        task.thumbnail = ssp.getTaskThumbnail(task.key.id);
+                        if (task.thumbnail != null) {
+                            task.thumbnail.setHasAlpha(false);
+                            mThumbnailCache.put(task.key, task.thumbnail);
+                        } else {
+                            task.thumbnail = mDefaultThumbnail;
+                        }
                     } else {
-                        task.thumbnail = mDefaultThumbnail;
+                        // Either the task has updated, or we haven't cached any information for the
+                        // task, so reload it
+                        tasksToForceLoad.add(task);
                     }
                 }
             }
@@ -536,7 +502,7 @@
         return root;
     }
 
-    /** Acquires the task resource data from the pool. */
+    /** Acquires the task resource data directly from the pool. */
     public void loadTaskData(Task t) {
         Drawable applicationIcon = mApplicationIconCache.get(t.key);
         Bitmap thumbnail = mThumbnailCache.get(t.key);
@@ -559,7 +525,7 @@
         if (requiresLoad) {
             mLoadQueue.addTask(t, false);
         }
-        t.notifyTaskDataLoaded(thumbnail, applicationIcon, false);
+        t.notifyTaskDataLoaded(thumbnail, applicationIcon);
     }
 
     /** Releases the task resource data back into the pool. */
@@ -613,7 +579,7 @@
      * Handles signals from the system, trimming memory when requested to prevent us from running
      * out of memory.
      */
-    void onTrimMemory(int level) {
+    public void onTrimMemory(int level) {
         if (Console.Enabled) {
             Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
                     Console.trimMemoryLevelToString(level));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index f366ef0..6b63b61 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.recents.model;
 
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import com.android.systemui.recents.Utilities;
 
 
 /**
@@ -30,7 +30,7 @@
     /* Task callbacks */
     public interface TaskCallbacks {
         /* Notifies when a task has been bound */
-        public void onTaskDataLoaded(boolean reloadingTaskData);
+        public void onTaskDataLoaded();
         /* Notifies when a task has been unbound */
         public void onTaskDataUnloaded();
     }
@@ -40,11 +40,17 @@
         public final int id;
         public final Intent baseIntent;
         public final int userId;
+        public long lastActiveTime;
 
-        public TaskKey(int id, Intent intent, int userId) {
+        public TaskKey(int id, Intent intent, int userId, long lastActiveTime) {
             this.id = id;
             this.baseIntent = intent;
             this.userId = userId;
+            this.lastActiveTime = lastActiveTime;
+        }
+
+        public void updateLastActiveTime(long lastActiveTime) {
+            this.lastActiveTime = lastActiveTime;
         }
 
         @Override
@@ -64,7 +70,8 @@
         @Override
         public String toString() {
             return "Task.Key: " + id + ", "
-                    + "u" + userId + ", "
+                    + "u: " + userId + ", "
+                    + "lat: " + lastActiveTime + ", "
                     + baseIntent.getComponent().getPackageName();
         }
     }
@@ -74,6 +81,7 @@
     public Drawable activityIcon;
     public String activityLabel;
     public int colorPrimary;
+    public int colorPrimaryGreyscale;
     public Bitmap thumbnail;
     public boolean isActive;
     public int userId;
@@ -85,11 +93,12 @@
     }
 
     public Task(int id, boolean isActive, Intent intent, String activityTitle,
-                BitmapDrawable activityIcon, int colorPrimary, int userId) {
-        this.key = new TaskKey(id, intent, userId);
+                BitmapDrawable activityIcon, int colorPrimary, int userId, long lastActiveTime) {
+        this.key = new TaskKey(id, intent, userId, lastActiveTime);
         this.activityLabel = activityTitle;
         this.activityIcon = activityIcon;
         this.colorPrimary = colorPrimary;
+        this.colorPrimaryGreyscale = Utilities.colorToGreyscale(colorPrimary);
         this.isActive = isActive;
         this.userId = userId;
     }
@@ -100,12 +109,11 @@
     }
 
     /** Notifies the callback listeners that this task has been loaded */
-    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon,
-                                     boolean reloadingTaskData) {
+    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
         this.applicationIcon = applicationIcon;
         this.thumbnail = thumbnail;
         if (mCb != null) {
-            mCb.onTaskDataLoaded(reloadingTaskData);
+            mCb.onTaskDataLoaded();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 8afc5b9..3856311 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.UserHandle;
@@ -36,8 +37,8 @@
 import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsPackageMonitor;
-import com.android.systemui.recents.RecentsTaskLoader;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -60,6 +61,7 @@
 
     RecentsConfiguration mConfig;
     LayoutInflater mInflater;
+    Paint mDebugModePaint;
 
     // The space partitioning root of this container
     SpaceNode mBSP;
@@ -107,6 +109,15 @@
             addView(stackView);
             mHasTasks |= (stack.getTaskCount() > 0);
         }
+
+        // Enable debug mode drawing
+        if (mConfig.debugModeEnabled) {
+            mDebugModePaint = new Paint();
+            mDebugModePaint.setColor(0xFFff0000);
+            mDebugModePaint.setStyle(Paint.Style.STROKE);
+            mDebugModePaint.setStrokeWidth(5f);
+            setWillNotDraw(false);
+        }
     }
 
     /** Launches the focused task from the first stack if possible */
@@ -322,6 +333,29 @@
         }
     }
 
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        // Debug mode drawing
+        if (mConfig.debugModeEnabled) {
+            canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugModePaint);
+        }
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        if (Console.Enabled) {
+            Console.log(Constants.Log.UI.MeasureAndLayout,
+                    "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
+        }
+
+        // Update the configuration with the latest system insets and trigger a relayout
+        mConfig.updateSystemInsets(insets.getSystemWindowInsets());
+        requestLayout();
+
+        return insets.consumeSystemWindowInsets(false, false, false, true);
+    }
+
     /** Notifies each task view of the user interaction. */
     public void onUserInteraction() {
         // Get the first stack view
@@ -354,29 +388,6 @@
         }
     }
 
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.Draw, "[RecentsView|dispatchDraw]", "",
-                    Console.AnsiPurple);
-        }
-        super.dispatchDraw(canvas);
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout,
-                    "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
-        }
-
-        // Update the configuration with the latest system insets and trigger a relayout
-        mConfig.updateSystemInsets(insets.getSystemWindowInsets());
-        requestLayout();
-
-        return insets.consumeSystemWindowInsets(false, false, false, true);
-    }
-
     /** Unfilters any filtered stacks */
     public boolean unfilterFilteredStacks() {
         if (mBSP != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 80f804d..2c0dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -90,11 +90,11 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (Constants.DebugFlags.App.EnableTaskBarTouchEvents) {
-            return super.onTouchEvent(event);
-        }
         // We ignore taps on the task bar except on the filter and dismiss buttons
-        return true;
+        if (!Constants.DebugFlags.App.EnableTaskBarTouchEvents) return true;
+        if (mConfig.debugModeEnabled) return true;
+
+        return super.onTouchEvent(event);
     }
 
     @Override
@@ -149,7 +149,7 @@
     }
 
     /** Binds the bar view to the task */
-    void rebindToTask(Task t, boolean animate) {
+    void rebindToTask(Task t) {
         mTask = t;
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
@@ -160,15 +160,12 @@
         }
         mActivityDescription.setText(t.activityLabel);
         // Try and apply the system ui tint
-        int tint = t.colorPrimary;
-        if (!Constants.DebugFlags.App.EnableTaskBarThemeColors || tint == 0) {
-            tint = mConfig.taskBarViewDefaultBackgroundColor;
-        }
-        setBackgroundColor(tint);
-        mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
-                mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
-        mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
-                mLightDismissDrawable, mDarkDismissDrawable));
+        setBackgroundColor(t.colorPrimary);
+        mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColorGreyscale(
+                t.colorPrimaryGreyscale, mConfig.taskBarViewLightTextColor,
+                mConfig.taskBarViewDarkTextColor));
+        mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColorGreyscale(
+                t.colorPrimaryGreyscale, mLightDismissDrawable, mDarkDismissDrawable));
     }
 
     /** Unbinds the bar view from the task */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 1cf28b9..452665a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -36,10 +36,10 @@
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.DozeTrigger;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsPackageMonitor;
-import com.android.systemui.recents.RecentsTaskLoader;
 import com.android.systemui.recents.ReferenceCountedTrigger;
 import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
@@ -935,6 +935,11 @@
         mUIDozeTrigger.poke();
     }
 
+    /** Disables handling touch on this task view. */
+    void setTouchOnTaskView(TaskView tv, boolean enabled) {
+        tv.setOnClickListener(enabled ? this : null);
+    }
+
     /**** TaskStackCallbacks Implementation ****/
 
     @Override
@@ -1177,47 +1182,34 @@
     @Override
     public void prepareViewToEnterPool(TaskView tv) {
         Task task = tv.getTask();
-        tv.resetViewProperties();
         if (Console.Enabled) {
             Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|returnToPool]",
                     tv.getTask() + " tv: " + tv);
         }
 
         // Report that this tasks's data is no longer being used
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        loader.unloadTaskData(task);
+        RecentsTaskLoader.getInstance().unloadTaskData(task);
 
         // Detach the view from the hierarchy
         detachViewFromParent(tv);
 
-        // Disable hw layers on this view
+        // Disable HW layers
         tv.disableHwLayers();
+
+        // Reset the view properties
+        tv.resetViewProperties();
     }
 
     @Override
-    public void prepareViewToLeavePool(TaskView tv, Task prepareData, boolean isNewView) {
+    public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
         if (Console.Enabled) {
             Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|leavePool]",
                     "isNewView: " + isNewView);
         }
 
-        // Setup and attach the view to the window
-        Task task = prepareData;
-        // We try and rebind the task (this MUST be done before the task filled)
+        // Rebind the task and request that this task's data be filled into the TaskView
         tv.onTaskBound(task);
-        // Request that this tasks's data be filled
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        loader.loadTaskData(task);
-        // Find the index where this task should be placed in the children
-        int insertIndex = -1;
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            Task tvTask = ((TaskView) getChildAt(i)).getTask();
-            if (mStack.containsTask(task) && (mStack.indexOfTask(task) < mStack.indexOfTask(tvTask))) {
-                insertIndex = i;
-                break;
-            }
-        }
+        RecentsTaskLoader.getInstance().loadTaskData(task);
 
         // Sanity check, the task view should always be clipping against the stack at this point,
         // but just in case, re-enable it here
@@ -1228,6 +1220,20 @@
             tv.setNoUserInteractionState();
         }
 
+        // Find the index where this task should be placed in the stack
+        int insertIndex = -1;
+        int taskIndex = mStack.indexOfTask(task);
+        if (taskIndex != -1) {
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                Task tvTask = ((TaskView) getChildAt(i)).getTask();
+                if (taskIndex < mStack.indexOfTask(tvTask)) {
+                    insertIndex = i;
+                    break;
+                }
+            }
+        }
+
         // Add/attach the view to the hierarchy
         if (Console.Enabled) {
             Console.log(Constants.Log.ViewPool.PoolCallbacks, "  [TaskStackView|insertIndex]",
@@ -1237,7 +1243,7 @@
             addView(tv, insertIndex);
 
             // Set the callbacks and listeners for this new view
-            tv.setOnClickListener(this);
+            setTouchOnTaskView(tv, true);
             tv.setCallbacks(this);
         } else {
             attachViewToParent(tv, insertIndex, tv.getLayoutParams());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index d2f18ae..f9c5f13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -371,11 +371,13 @@
 
     @Override
     public void onBeginDrag(View v) {
-        // Enable HW layers
-        mSv.addHwLayersRefCount("swipeBegin");
-        // Disable clipping with the stack while we are swiping
         TaskView tv = (TaskView) v;
+        // Disable clipping with the stack while we are swiping
         tv.setClipViewInStack(false);
+        // Enable HW layers on that task
+        tv.enableHwLayers();
+        // Disallow touch events from this task view
+        mSv.setTouchOnTaskView(tv, false);
         // Disallow parents from intercepting touch events
         final ViewParent parent = mSv.getParent();
         if (parent != null) {
@@ -391,25 +393,31 @@
     @Override
     public void onChildDismissed(View v) {
         TaskView tv = (TaskView) v;
-        mSv.onTaskDismissed(tv);
-
+        // Disable HW layers on that task
+        if (mSv.mHwLayersTrigger.getCount() == 0) {
+            tv.disableHwLayers();
+        }
         // Re-enable clipping with the stack (we will reuse this view)
         tv.setClipViewInStack(true);
-
-        // Disable HW layers
-        mSv.decHwLayersRefCount("swipeComplete");
+        // Remove the task view from the stack
+        mSv.onTaskDismissed(tv);
     }
 
     @Override
     public void onSnapBackCompleted(View v) {
-        // Re-enable clipping with the stack
-        TaskView tv = (TaskView) v;
-        tv.setClipViewInStack(true);
+        onDragCancelled(v);
     }
 
     @Override
     public void onDragCancelled(View v) {
-        // Disable HW layers
-        mSv.decHwLayersRefCount("swipeCancelled");
+        TaskView tv = (TaskView) v;
+        // Disable HW layers on that task
+        if (mSv.mHwLayersTrigger.getCount() == 0) {
+            tv.disableHwLayers();
+        }
+        // Re-enable clipping with the stack
+        tv.setClipViewInStack(true);
+        // Re-enable touch events from this task view
+        mSv.setTouchOnTaskView(tv, true);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index c2b2094..636746d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -83,7 +83,7 @@
     }
 
     /** Binds the thumbnail view to the task */
-    void rebindToTask(Task t, boolean animate) {
+    void rebindToTask(Task t) {
         mTask = t;
         if (t.thumbnail != null) {
             setImageBitmap(t.thumbnail);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index cfba74c..f9d5a65 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -123,7 +123,7 @@
         mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
 
         if (mTaskDataLoaded) {
-            onTaskDataLoaded(false);
+            onTaskDataLoaded();
         }
     }
 
@@ -551,11 +551,11 @@
     }
 
     @Override
-    public void onTaskDataLoaded(boolean reloadingTaskData) {
+    public void onTaskDataLoaded() {
         if (mThumbnailView != null && mBarView != null) {
             // Bind each of the views to the new task data
-            mThumbnailView.rebindToTask(mTask, reloadingTaskData);
-            mBarView.rebindToTask(mTask, reloadingTaskData);
+            mThumbnailView.rebindToTask(mTask);
+            mBarView.rebindToTask(mTask);
             // Rebind any listeners
             mBarView.mApplicationIcon.setOnClickListener(this);
             mBarView.mDismissButton.setOnClickListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index f6f78e9..1550217 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -25,6 +25,8 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -89,6 +91,8 @@
             = new PathInterpolator(0, 0, 0.5f, 1);
 
     private boolean mDimmed;
+    private boolean mDark;
+    private final Paint mDarkPaint = createDarkPaint();
 
     private int mBgResId = com.android.internal.R.drawable.notification_material_bg;
     private int mDimmedBgResId = com.android.internal.R.drawable.notification_material_bg_dim;
@@ -295,6 +299,34 @@
         }
     }
 
+    public void setDark(boolean dark, boolean fade) {
+        // TODO implement fade
+        if (mDark != dark) {
+            mDark = dark;
+            if (mDark) {
+                setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+            } else {
+                setLayerType(View.LAYER_TYPE_NONE, null);
+            }
+        }
+    }
+
+    private static Paint createDarkPaint() {
+        final Paint p = new Paint();
+        final float[] invert = {
+            -1f,  0f,  0f, 1f, 1f,
+             0f, -1f,  0f, 1f, 1f,
+             0f,  0f, -1f, 1f, 1f,
+             0f,  0f,  0f, 1f, 0f
+        };
+        final ColorMatrix m = new ColorMatrix(invert);
+        final ColorMatrix grayscale = new ColorMatrix();
+        grayscale.setSaturation(0);
+        m.preConcat(grayscale);
+        p.setColorFilter(new ColorMatrixColorFilter(m));
+        return p;
+    }
+
     /**
      * Sets the resource id for the background of this notification.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 4d4a8ab..c3fb83c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -167,6 +167,15 @@
     }
 
     /**
+     * Sets the notification as dark. The default implementation does nothing.
+     *
+     * @param dark Whether the notification should be dark.
+     * @param fade Whether an animation should be played to change the state.
+     */
+    public void setDark(boolean dark, boolean fade) {
+    }
+
+    /**
      * @return The desired notification height.
      */
     public int getIntrinsicHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a6ce5d5..cec7592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -109,7 +109,6 @@
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
             new KeyguardClockPositionAlgorithm.Result();
-    private boolean mIsSwipedHorizontally;
     private boolean mIsExpanding;
 
     private boolean mBlockTouches;
@@ -122,18 +121,7 @@
     }
 
     public void setStatusBar(PhoneStatusBar bar) {
-        if (mStatusBar != null) {
-            mStatusBar.setOnFlipRunnable(null);
-        }
         mStatusBar = bar;
-        if (bar != null) {
-            mStatusBar.setOnFlipRunnable(new Runnable() {
-                @Override
-                public void run() {
-                    requestPanelHeightUpdate();
-                }
-            });
-        }
     }
 
     @Override
@@ -302,6 +290,8 @@
         mUnlockIconActive = false;
         mPageSwiper.reset();
         closeQs();
+        mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
+                true /* cancelAnimators */);
     }
 
     public void closeQs() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index b94f6f3..b4faaaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -188,10 +188,10 @@
                 pv.collapse();
                 waiting = true;
             } else {
+                pv.resetViews();
                 pv.setExpandedFraction(0); // just in case
                 pv.setVisibility(View.GONE);
                 pv.cancelPeek();
-                pv.resetViews();
             }
         }
         if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 00951b2..62ac54e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -64,7 +64,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
@@ -100,6 +99,7 @@
 import com.android.systemui.DemoMode;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.doze.DozeService;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.qs.CircularClipper;
 import com.android.systemui.qs.QSPanel;
@@ -168,11 +168,6 @@
     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
     private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
 
-    /**
-     * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
-     */
-    private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;
-
     private static final int STATUS_OR_NAV_TRANSIENT =
             View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
     private static final long AUTOHIDE_TIMEOUT_MS = 3000;
@@ -185,21 +180,6 @@
      */
     private static final int HINT_RESET_DELAY_MS = 1200;
 
-    // fling gesture tuning parameters, scaled to display density
-    private float mSelfExpandVelocityPx; // classic value: 2000px/s
-    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
-    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
-    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
-    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
-    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
-    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
-
-    private float mExpandAccelPx; // classic value: 2000px/s/s
-    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
-
-    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
-                                                    // faster than mSelfCollapseVelocityPx)
-
     PhoneStatusBarPolicy mIconPolicy;
 
     // These are no longer handled by the policy, because we need custom strategies for them
@@ -225,6 +205,7 @@
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     private StatusBarWindowManager mStatusBarWindowManager;
     private UnlockMethodCache mUnlockMethodCache;
+    private DozeServiceHost mDozeServiceHost;
 
     int mPixelFormat;
     Object mQueueLock = new Object();
@@ -397,14 +378,9 @@
             }
         }};
 
-    private Runnable mOnFlipRunnable;
-    private VelocityTracker mSettingsTracker;
-    private float mSettingsDownY;
-    private boolean mSettingsStarted;
-    private boolean mSettingsCancelled;
-    private boolean mSettingsClosing;
     private boolean mVisible;
     private boolean mWaitingForKeyguardExit;
+    private boolean mDozing;
 
     private Interpolator mLinearOutSlowIn;
     private Interpolator mAlphaOut = new PathInterpolator(0f, 0.4f, 1f, 1f);
@@ -420,14 +396,12 @@
 
     private int mDisabledUnmodified;
 
-    public void setOnFlipRunnable(Runnable onFlipRunnable) {
-        mOnFlipRunnable = onFlipRunnable;
-    }
-
     /** Keys of notifications currently visible to the user. */
     private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
     private long mLastVisibilityReportUptimeMs;
 
+    private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
+
     private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
             | ViewState.LOCATION_TOP_STACK_PEEKING
             | ViewState.LOCATION_MAIN_AREA
@@ -537,6 +511,9 @@
         }
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         startKeyguard();
+
+        mDozeServiceHost = new DozeServiceHost();
+        putComponent(DozeService.Host.class, mDozeServiceHost);
     }
 
     // ================================================================================
@@ -1232,7 +1209,6 @@
         for (View remove : toRemove) {
             mStackScroller.removeView(remove);
         }
-
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
@@ -1265,6 +1241,7 @@
         updateRowStates();
         updateSpeedbump();
         mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
+        mShadeUpdates.check();
     }
 
     private void updateSpeedbump() {
@@ -1627,10 +1604,6 @@
         }
     }
 
-    private Handler getHandler() {
-        return mHandler;
-    }
-
     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
         public void onFocusChange(View v, boolean hasFocus) {
             // Because 'v' is a ViewGroup, all its children will be (un)selected
@@ -1775,12 +1748,6 @@
         if (false) postStartTracing();
     }
 
-    private static void cancelAnim(Animator anim) {
-        if (anim != null) {
-            anim.cancel();
-        }
-    }
-
     public void flipToNotifications(boolean animate) {
         // TODO: Animation
         mNotificationPanel.closeQs();
@@ -2273,6 +2240,7 @@
         pw.println(windowStateToString(mStatusBarWindowState));
         pw.print("  mStatusBarMode=");
         pw.println(BarTransitions.modeToString(mStatusBarMode));
+        pw.print("  mDozing="); pw.println(mDozing);
         pw.print("  mZenMode=");
         pw.println(Settings.Global.zenModeToString(mZenMode));
         pw.print("  mUseHeadsUp=");
@@ -2407,28 +2375,6 @@
         }
     }
 
-    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            synchronized (mNotificationData) {
-                mPostCollapseCleanup = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (DEBUG) {
-                            Log.v(TAG, "running post-collapse cleanup");
-                        }
-                        try {
-                            mBarService.onClearAllNotifications(mCurrentUserId);
-                        } catch (Exception ex) { }
-                    }
-                };
-
-                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                return;
-                // TODO: Handle this better with notification stack scroller
-            }
-        }
-    };
-
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned) {
         if (onlyProvisioned && !isDeviceProvisioned()) return;
 
@@ -2456,12 +2402,6 @@
         }
     };
 
-    private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            animateExpandNotificationsPanel();
-        }
-    };
-
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             if (DEBUG) Log.v(TAG, "onReceive: " + intent);
@@ -2595,21 +2535,6 @@
 
         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
 
-        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
-        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
-        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
-        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
-
-        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
-        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
-
-        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
-        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
-
-        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
-
-        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
-
         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
         if (mNotificationPanelGravity <= 0) {
             mNotificationPanelGravity = Gravity.START | Gravity.TOP;
@@ -2675,7 +2600,6 @@
         if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
             return;
         }
-
         String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
         String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
         try {
@@ -2950,6 +2874,7 @@
             mNotificationPanel.setKeyguardShowing(false);
             mScrimController.setKeyguardShowing(false);
         }
+        updateDozingState();
         updateStackScrollerState();
         updatePublicMode();
         updateNotifications();
@@ -2957,6 +2882,26 @@
         updateCarrierLabelVisibility(false);
     }
 
+    private void updateDozingState() {
+        final boolean bottomGone = mKeyguardBottomArea.getVisibility() == View.GONE;
+        if (mDozing) {
+            mNotificationPanel.setBackgroundColor(0xff000000);
+            mHeader.setVisibility(View.INVISIBLE);
+            if (!bottomGone) {
+                mKeyguardBottomArea.setVisibility(View.INVISIBLE);
+            }
+            mStackScroller.setDark(true, false /*animate*/);
+        } else {
+            mNotificationPanel.setBackground(null);
+            mHeader.setVisibility(View.VISIBLE);
+            if (!bottomGone) {
+                mKeyguardBottomArea.setVisibility(View.VISIBLE);
+            }
+            mStackScroller.setDark(false, false /*animate*/);
+        }
+        mScrimController.setDozing(mDozing);
+    }
+
     public void updateStackScrollerState() {
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
@@ -3229,4 +3174,121 @@
         }
         notifyUiVisibilityChanged(mSystemUiVisibility);
     }
+
+    private final class ShadeUpdates {
+        private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
+        private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
+
+        public void check() {
+            mNewVisibleNotifications.clear();
+            for (int i = 0; i < mNotificationData.size(); i++) {
+                final Entry entry = mNotificationData.get(i);
+                final boolean visible = entry.row != null
+                        && entry.row.getVisibility() == View.VISIBLE;
+                if (visible) {
+                    mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
+                }
+            }
+            final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
+            mVisibleNotifications.clear();
+            mVisibleNotifications.putAll(mNewVisibleNotifications);
+
+            // We have new notifications
+            if (updates && mDozeServiceHost != null) {
+                mDozeServiceHost.fireNewNotifications();
+            }
+        }
+    }
+
+    private final class DozeServiceHost implements DozeService.Host {
+        // Amount of time to allow to update the time shown on the screen before releasing
+        // the wakelock.  This timeout is design to compensate for the fact that we don't
+        // currently have a way to know when time display contents have actually been
+        // refreshed once we've finished rendering a new frame.
+        private static final long PROCESSING_TIME = 500;
+
+        private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+        private final H mHandler = new H();
+
+        private DozeService mCurrentDozeService;
+
+        public void fireNewNotifications() {
+            for (Callback callback : mCallbacks) {
+                callback.onNewNotifications();
+            }
+        }
+
+        @Override
+        public void addCallback(Callback callback) {
+            mCallbacks.add(callback);
+        }
+
+        @Override
+        public void removeCallback(Callback callback) {
+            mCallbacks.remove(callback);
+        }
+
+        @Override
+        public void requestDoze(DozeService dozeService) {
+            if (dozeService == null) return;
+            dozeService.stayAwake(PROCESSING_TIME);
+            mHandler.obtainMessage(H.REQUEST_DOZE, dozeService).sendToTarget();
+        }
+
+        @Override
+        public void requestTease(DozeService dozeService) {
+            if (dozeService == null) return;
+            dozeService.stayAwake(PROCESSING_TIME);
+            mHandler.obtainMessage(H.REQUEST_TEASE, dozeService).sendToTarget();
+        }
+
+        @Override
+        public void dozingStopped(DozeService dozeService) {
+            if (dozeService == null) return;
+            dozeService.stayAwake(PROCESSING_TIME);
+            mHandler.obtainMessage(H.DOZING_STOPPED, dozeService).sendToTarget();
+        }
+
+        private void handleRequestDoze(DozeService dozeService) {
+            mCurrentDozeService = dozeService;
+            if (!mDozing) {
+                mDozing = true;
+                updateDozingState();
+            }
+            mCurrentDozeService.startDozing();
+        }
+
+        private void handleRequestTease(DozeService dozeService) {
+            if (!dozeService.equals(mCurrentDozeService)) return;
+            final long stayAwake = mScrimController.tease();
+            mCurrentDozeService.stayAwake(stayAwake);
+        }
+
+        private void handleDozingStopped(DozeService dozeService) {
+            if (dozeService.equals(mCurrentDozeService)) {
+                mCurrentDozeService = null;
+            }
+            if (mDozing) {
+                mDozing = false;
+                updateDozingState();
+            }
+        }
+
+        private final class H extends Handler {
+            private static final int REQUEST_DOZE = 1;
+            private static final int REQUEST_TEASE = 2;
+            private static final int DOZING_STOPPED = 3;
+
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == REQUEST_DOZE) {
+                    handleRequestDoze((DozeService) msg.obj);
+                } else if (msg.what == REQUEST_TEASE) {
+                    handleRequestTease((DozeService) msg.obj);
+                } else if (msg.what == DOZING_STOPPED) {
+                    handleDozingStopped((DozeService) msg.obj);
+                }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1264d75..bf63f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -21,21 +21,35 @@
 import android.animation.ValueAnimator;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
+import com.android.systemui.R;
+
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
  * security method gets shown).
  */
 public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
+    private static final String TAG = "ScrimController";
+    private static final boolean DEBUG = false;
 
     private static final float SCRIM_BEHIND_ALPHA = 0.62f;
     private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.5f;
     private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
     private static final long ANIMATION_DURATION = 220;
+    private static final int TAG_KEY_ANIM = R.id.scrim;
+
+    private static final int NUM_TEASES = 3;
+    private static final long TEASE_IN_ANIMATION_DURATION = 500;
+    private static final long TEASE_VISIBLE_DURATION = 3000;
+    private static final long TEASE_OUT_ANIMATION_DURATION = 1000;
+    private static final long TEASE_INVISIBLE_DURATION = 1000;
+    private static final long TEASE_DURATION = TEASE_IN_ANIMATION_DURATION
+            + TEASE_VISIBLE_DURATION + TEASE_OUT_ANIMATION_DURATION + TEASE_INVISIBLE_DURATION;
 
     private final View mScrimBehind;
     private final View mScrimInFront;
@@ -54,6 +68,8 @@
     private long mAnimationDelay;
     private Runnable mOnAnimationFinished;
     private boolean mAnimationStarted;
+    private boolean mDozing;
+    private int mTeasesRemaining;
 
     private final Interpolator mInterpolator = new DecelerateInterpolator();
 
@@ -97,6 +113,29 @@
         scheduleUpdate();
     }
 
+    public void setDozing(boolean dozing) {
+        if (mDozing == dozing) return;
+        mDozing = dozing;
+        if (!mDozing) {
+            cancelTeasing();
+        }
+        scheduleUpdate();
+    }
+
+    /** When dozing, fade screen contents in and out a few times using the front scrim. */
+    public long tease() {
+        if (!mDozing) return 0;
+        mTeasesRemaining = NUM_TEASES;
+        mScrimInFront.post(mTeaseIn);
+        return NUM_TEASES * TEASE_DURATION;
+    }
+
+    private void cancelTeasing() {
+        mTeasesRemaining = 0;
+        mScrimInFront.removeCallbacks(mTeaseIn);
+        mScrimInFront.removeCallbacks(mTeaseOut);
+    }
+
     private void scheduleUpdate() {
         if (mUpdatePending) return;
 
@@ -125,6 +164,8 @@
         } else if (mBouncerShowing) {
             setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
             setScrimBehindColor(0f);
+        } else if (mDozing) {
+            setScrimInFrontColor(1);
         } else {
             setScrimInFrontColor(0f);
             setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
@@ -174,6 +215,10 @@
         if (current == targetColor) {
             return;
         }
+        Object runningAnim = scrim.getTag(TAG_KEY_ANIM);
+        if (runningAnim instanceof ValueAnimator) {
+            ((ValueAnimator) runningAnim).cancel();
+        }
         ValueAnimator anim = ValueAnimator.ofInt(current, target);
         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
@@ -193,9 +238,11 @@
                     mOnAnimationFinished.run();
                     mOnAnimationFinished = null;
                 }
+                scrim.setTag(TAG_KEY_ANIM, null);
             }
         });
         anim.start();
+        scrim.setTag(TAG_KEY_ANIM, anim);
         mAnimationStarted = true;
     }
 
@@ -225,4 +272,51 @@
         mAnimationStarted = false;
         return true;
     }
+
+    private final Runnable mTeaseIn = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Log.d(TAG, "Tease in, mDozing=" + mDozing
+                    + " mTeasesRemaining=" + mTeasesRemaining);
+            if (!mDozing || mTeasesRemaining == 0) return;
+            mTeasesRemaining--;
+            mDurationOverride = TEASE_IN_ANIMATION_DURATION;
+            mAnimationDelay = 0;
+            mAnimateChange = true;
+            mOnAnimationFinished = mTeaseInFinished;
+            setScrimColor(mScrimInFront, 0);
+        }
+    };
+
+    private final Runnable mTeaseInFinished = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing);
+            if (!mDozing) return;
+            mScrimInFront.postDelayed(mTeaseOut, TEASE_VISIBLE_DURATION);
+        }
+    };
+
+    private final Runnable mTeaseOut = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Log.d(TAG, "Tease in finished, mDozing=" + mDozing);
+            if (!mDozing) return;
+            mDurationOverride = TEASE_OUT_ANIMATION_DURATION;
+            mAnimationDelay = 0;
+            mAnimateChange = true;
+            mOnAnimationFinished = mTeaseOutFinished;
+            setScrimColor(mScrimInFront, 1);
+        }
+    };
+
+    private final Runnable mTeaseOutFinished = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Log.d(TAG, "Tease out finished, mTeasesRemaining=" + mTeasesRemaining);
+            if (mTeasesRemaining > 0) {
+                mScrimInFront.postDelayed(mTeaseIn, TEASE_INVISIBLE_DURATION);
+            }
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index fcc951e..0582140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -33,6 +33,7 @@
     private float mOverScrollBottomAmount;
     private int mSpeedBumpIndex = -1;
     private float mScrimAmount;
+    private boolean mDark;
 
     public int getScrollY() {
         return mScrollY;
@@ -62,6 +63,11 @@
         mDimmed = dimmed;
     }
 
+    /** In dark mode, we draw as little as possible, assuming a black background */
+    public void setDark(boolean dark) {
+        mDark = dark;
+    }
+
     /**
      * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
      * interaction. This child is then scaled normally and its background is fully opaque.
@@ -74,6 +80,10 @@
         return mDimmed;
     }
 
+    public boolean isDark() {
+        return mDark;
+    }
+
     public ActivatableNotificationView getActivatedChild() {
         return mActivatedChild;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index cf56fa57..99d3a01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -29,6 +29,7 @@
     boolean animateHeight;
     boolean animateTopInset;
     boolean animateDimmed;
+    boolean animateDark;
     boolean hasDelays;
 
     public AnimationFilter animateAlpha() {
@@ -71,6 +72,11 @@
         return this;
     }
 
+    public AnimationFilter animateDark() {
+        animateDark = true;
+        return this;
+    }
+
     /**
      * Combines multiple filters into {@code this} filter, using or as the operand .
      *
@@ -92,6 +98,7 @@
         animateHeight |= filter.animateHeight;
         animateTopInset |= filter.animateTopInset;
         animateDimmed |= filter.animateDimmed;
+        animateDark |= filter.animateDark;
         hasDelays |= filter.hasDelays;
     }
 
@@ -103,6 +110,7 @@
         animateHeight = false;
         animateTopInset = false;
         animateDimmed = false;
+        animateDark = false;
         hasDelays = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index f6e9aef..4220efe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -128,6 +128,7 @@
     private boolean mNeedsAnimation;
     private boolean mTopPaddingNeedsAnimation;
     private boolean mDimmedNeedsAnimation;
+    private boolean mDarkNeedsAnimation;
     private boolean mActivateNeedsAnimation;
     private boolean mIsExpanded = true;
     private boolean mChildrenUpdateRequested;
@@ -356,14 +357,6 @@
         return getNotGoneChildCount() > 1;
     }
 
-    private boolean isViewExpanded(View view) {
-        if (view != null) {
-            ExpandableView expandView = (ExpandableView) view;
-            return expandView.getActualHeight() > mCollapsedSize;
-        }
-        return false;
-    }
-
     /**
      * Updates the children views according to the stack scroll algorithm. Call this whenever
      * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
@@ -1479,6 +1472,7 @@
         generateTopPaddingEvent();
         generateActivateEvent();
         generateDimmedEvent();
+        generateDarkEvent();
         mNeedsAnimation = false;
     }
 
@@ -1554,6 +1548,14 @@
         mDimmedNeedsAnimation = false;
     }
 
+    private void generateDarkEvent() {
+        if (mDarkNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DARK));
+        }
+        mDarkNeedsAnimation = false;
+    }
+
     private boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
@@ -1852,6 +1854,18 @@
     }
 
     /**
+     * See {@link AmbientState#setDark}.
+     */
+    public void setDark(boolean dark, boolean animate) {
+        mAmbientState.setDark(dark);
+        if (animate && mAnimationsEnabled) {
+            mDarkNeedsAnimation = true;
+            mNeedsAnimation =  true;
+        }
+        requestChildrenUpdate();
+    }
+
+    /**
      * A listener that is notified when some child locations might have changed.
      */
     public interface OnChildLocationsChangedListener {
@@ -1940,7 +1954,11 @@
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
-                        .animateZ()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_DARK
+                new AnimationFilter()
+                        .animateDark(),
         };
 
         static int[] LENGTHS = new int[] {
@@ -1971,6 +1989,9 @@
 
                 // ANIMATION_TYPE_CHANGE_POSITION
                 StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_DARK
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
         };
 
         static final int ANIMATION_TYPE_ADD = 0;
@@ -1982,6 +2003,7 @@
         static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
         static final int ANIMATION_TYPE_DIMMED = 7;
         static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
+        static final int ANIMATION_TYPE_DARK = 9;
 
         final long eventStartTime;
         final View changingView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 9a4b798..4956fe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -233,12 +233,14 @@
     private void updateDimmedActivated(AmbientState ambientState, StackScrollState resultState,
             StackScrollAlgorithmState algorithmState) {
         boolean dimmed = ambientState.isDimmed();
+        boolean dark = ambientState.isDark();
         View activatedChild = ambientState.getActivatedChild();
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             View child = algorithmState.visibleChildren.get(i);
             StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
             childViewState.dimmed = dimmed;
+            childViewState.dark = dark;
             boolean isActivatedChild = activatedChild == child;
             childViewState.scale = !dimmed || isActivatedChild
                     ? 1.0f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 02f2cd6..d8407d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -148,6 +148,9 @@
                 // apply dimming
                 child.setDimmed(state.dimmed, false /* animate */);
 
+                // apply dark
+                child.setDark(state.dark, false /* animate */);
+
                 // apply scrimming
                 child.setScrimAmount(state.scrimAmount);
 
@@ -224,6 +227,7 @@
         boolean gone;
         float scale;
         boolean dimmed;
+        boolean dark;
 
         /**
          * A value between 0 and 1 indicating how much the view should be scrimmed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 0006dad..5efbc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -176,6 +176,9 @@
         // start dimmed animation
         child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
 
+        // start dark animation
+        child.setDark(viewState.dark, mAnimationFilter.animateDark);
+
         // apply scrimming
         child.setScrimAmount(viewState.scrimAmount);
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 1cab7ea..d514c99 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -34,6 +34,9 @@
 import android.media.AudioSystem;
 import android.media.RingtoneManager;
 import android.media.ToneGenerator;
+import android.media.VolumeProvider;
+import android.media.session.MediaController;
+import android.media.session.MediaController.VolumeInfo;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -224,6 +227,7 @@
     /** Object that contains data for each slider */
     private class StreamControl {
         int streamType;
+        MediaController controller;
         ViewGroup group;
         ImageView icon;
         SeekBar seekbarView;
@@ -405,7 +409,8 @@
         if (streamType == STREAM_MASTER) {
             return mAudioManager.isMasterMute();
         } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
-            return (mAudioManager.getRemoteStreamVolume() <= 0);
+            // TODO do we need to support a distinct mute property for remote?
+            return false;
         } else {
             return mAudioManager.isStreamMute(streamType);
         }
@@ -415,7 +420,14 @@
         if (streamType == STREAM_MASTER) {
             return mAudioManager.getMasterMaxVolume();
         } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
-            return mAudioManager.getRemoteStreamMaxVolume();
+            if (mStreamControls != null) {
+                StreamControl sc = mStreamControls.get(streamType);
+                if (sc != null && sc.controller != null) {
+                    VolumeInfo vi = sc.controller.getVolumeInfo();
+                    return vi.getMaxVolume();
+                }
+            }
+            return -1;
         } else {
             return mAudioManager.getStreamMaxVolume(streamType);
         }
@@ -425,19 +437,32 @@
         if (streamType == STREAM_MASTER) {
             return mAudioManager.getMasterVolume();
         } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
-            return mAudioManager.getRemoteStreamVolume();
+            if (mStreamControls != null) {
+                StreamControl sc = mStreamControls.get(streamType);
+                if (sc != null && sc.controller != null) {
+                    VolumeInfo vi = sc.controller.getVolumeInfo();
+                    return vi.getCurrentVolume();
+                }
+            }
+            return -1;
         } else {
             return mAudioManager.getStreamVolume(streamType);
         }
     }
 
-    private void setStreamVolume(int streamType, int index, int flags) {
-        if (streamType == STREAM_MASTER) {
-            mAudioManager.setMasterVolume(index, flags);
-        } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
-            mAudioManager.setRemoteStreamVolume(index);
-        } else {
-            mAudioManager.setStreamVolume(streamType, index, flags);
+    private void setStreamVolume(StreamControl sc, int index, int flags) {
+        if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+            if (sc.controller != null) {
+                sc.controller.setVolumeTo(index, flags);
+            } else {
+                Log.wtf(mTag, "Adjusting remote volume without a controller!");
+            }
+        } else if (getStreamVolume(sc.streamType) != index) {
+            if (sc.streamType == STREAM_MASTER) {
+                mAudioManager.setMasterVolume(index, flags);
+            } else {
+                mAudioManager.setStreamVolume(sc.streamType, index, flags);
+            }
         }
     }
 
@@ -549,7 +574,7 @@
         if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
             // never disable touch interactions for remote playback, the muting is not tied to
             // the state of the phone.
-            sc.seekbarView.setEnabled(true);
+            sc.seekbarView.setEnabled(!fixedVolume);
         } else if (fixedVolume ||
                         (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
                         (sConfirmSafeVolumeDialog != null)) {
@@ -677,7 +702,7 @@
         obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
     }
 
-    public void postRemoteVolumeChanged(int streamType, int flags) {
+    public void postRemoteVolumeChanged(MediaController controller, int flags) {
         if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
         synchronized (this) {
             if (mStreamControls == null) {
@@ -685,7 +710,7 @@
             }
         }
         removeMessages(MSG_FREE_RESOURCES);
-        obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
+        obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
     }
 
     public void postRemoteSliderVisibility(boolean visible) {
@@ -758,7 +783,7 @@
                 if (mActiveStreamType != streamType) {
                     reorderSliders(streamType);
                 }
-                onShowVolumeChanged(streamType, flags);
+                onShowVolumeChanged(streamType, flags, null);
             }
         }
 
@@ -790,7 +815,7 @@
         onVolumeChanged(streamType, flags);
     }
 
-    protected void onShowVolumeChanged(int streamType, int flags) {
+    protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
         int index = getStreamVolume(streamType);
 
         mRingIsSilent = false;
@@ -803,6 +828,7 @@
         // get max volume for progress bar
 
         int max = getStreamMaxVolume(streamType);
+        StreamControl sc = mStreamControls.get(streamType);
 
         switch (streamType) {
 
@@ -865,13 +891,37 @@
             }
 
             case AudioService.STREAM_REMOTE_MUSIC: {
+                if (controller == null && sc != null) {
+                    // If we weren't passed one try using the last one set.
+                    controller = sc.controller;
+                }
+                if (controller == null) {
+                    // We still don't have one, ignore the command.
+                    Log.w(mTag, "sent remote volume change without a controller!");
+                } else {
+                    VolumeInfo vi = controller.getVolumeInfo();
+                    index = vi.getCurrentVolume();
+                    max = vi.getMaxVolume();
+                    if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
+                        // if the remote volume is fixed add the flag for the UI
+                        flags |= AudioManager.FLAG_FIXED_VOLUME;
+                    }
+                }
                 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
                 break;
             }
         }
 
-        StreamControl sc = mStreamControls.get(streamType);
         if (sc != null) {
+            if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
+                if (sc.controller != null) {
+                    sc.controller.removeCallback(mMediaControllerCb);
+                }
+                sc.controller = controller;
+                if (controller != null) {
+                    sc.controller.addCallback(mMediaControllerCb);
+                }
+            }
             if (sc.seekbarView.getMax() != max) {
                 sc.seekbarView.setMax(max);
             }
@@ -949,34 +999,21 @@
         mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
     }
 
-    protected void onRemoteVolumeChanged(int streamType, int flags) {
-        // streamType is the real stream type being affected, but for the UI sliders, we
-        // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
-        // stream type.
-        if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
+    protected void onRemoteVolumeChanged(MediaController controller, int flags) {
+        if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
+                    + ")");
 
         if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
             synchronized (this) {
                 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
                     reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
                 }
-                onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
+                onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
             }
         } else {
             if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
         }
 
-        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
-            removeMessages(MSG_PLAY_SOUND);
-            sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
-        }
-
-        if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
-            removeMessages(MSG_PLAY_SOUND);
-            removeMessages(MSG_VIBRATE);
-            onStopSounds();
-        }
-
         removeMessages(MSG_FREE_RESOURCES);
         sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
         resetTimeout();
@@ -987,10 +1024,24 @@
         if (isShowing()
                 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
                 && (mStreamControls != null)) {
-            onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
+            onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
         }
     }
 
+    /**
+     * Clear the current remote stream controller.
+     */
+    private void clearRemoteStreamController() {
+        if (mStreamControls != null) {
+            StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
+            if (sc != null) {
+                if (sc.controller != null) {
+                    sc.controller.removeCallback(mMediaControllerCb);
+                    sc.controller = null;
+                }
+            }
+        }
+    }
 
     /**
      * Handler for MSG_SLIDER_VISIBILITY_CHANGED
@@ -1137,6 +1188,7 @@
                 if (isShowing()) {
                     if (mDialog != null) {
                         mDialog.dismiss();
+                        clearRemoteStreamController();
                         mActiveStreamType = -1;
                     }
                 }
@@ -1155,7 +1207,7 @@
             }
 
             case MSG_REMOTE_VOLUME_CHANGED: {
-                onRemoteVolumeChanged(msg.arg1, msg.arg2);
+                onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
                 break;
             }
 
@@ -1202,9 +1254,7 @@
             final Object tag = seekBar.getTag();
             if (fromUser && tag instanceof StreamControl) {
                 StreamControl sc = (StreamControl) tag;
-                if (getStreamVolume(sc.streamType) != progress) {
-                    setStreamVolume(sc.streamType, progress, 0);
-                }
+                setStreamVolume(sc, progress, 0);
             }
             resetTimeout();
         }
@@ -1215,19 +1265,6 @@
 
         @Override
         public void onStopTrackingTouch(SeekBar seekBar) {
-            final Object tag = seekBar.getTag();
-            if (tag instanceof StreamControl) {
-                StreamControl sc = (StreamControl) tag;
-                // Because remote volume updates are asynchronous, AudioService
-                // might have received a new remote volume value since the
-                // finger adjusted the slider. So when the progress of the
-                // slider isn't being tracked anymore, adjust the slider to the
-                // last "published" remote volume value, so the UI reflects the
-                // actual volume.
-                if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
-                    seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
-                }
-            }
         }
     };
 
@@ -1257,4 +1294,10 @@
             postZenModeChanged(zen);
         }
     };
+
+    private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
+        public void onVolumeInfoChanged(VolumeInfo info) {
+            onRemoteVolumeUpdateIfShown();
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 7da90d8..c1f92ff 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -5,7 +5,11 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
+import android.media.IRemoteVolumeController;
 import android.media.IVolumeController;
+import android.media.session.ISessionController;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -42,12 +46,21 @@
 
     private final Handler mHandler = new Handler();
     private AudioManager mAudioManager;
+    private MediaSessionManager mMediaSessionManager;
     private VolumeController mVolumeController;
+    private RemoteVolumeController mRemoteVolumeController;
+
+    private VolumePanel mDialogPanel;
+    private VolumePanel mPanel;
 
     @Override
     public void start() {
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        mVolumeController = new VolumeController(mContext);
+        mMediaSessionManager = (MediaSessionManager) mContext
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
+        initPanel();
+        mVolumeController = new VolumeController();
+        mRemoteVolumeController = new RemoteVolumeController();
         putComponent(VolumeComponent.class, mVolumeController);
         updateController();
         mContext.getContentResolver().registerContentObserver(SETTING_URI, false, mObserver);
@@ -57,12 +70,32 @@
         if (Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT) != 0) {
             Log.d(TAG, "Registering volume controller");
             mAudioManager.setVolumeController(mVolumeController);
+            mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
         } else {
             Log.d(TAG, "Unregistering volume controller");
             mAudioManager.setVolumeController(null);
+            mMediaSessionManager.setRemoteVolumeController(null);
         }
     }
 
+    private void initPanel() {
+        mPanel = new VolumePanel(mContext, null, new ZenModeControllerImpl(mContext, mHandler));
+        final int delay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
+        mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+            @Override
+            public void onMoreSettings() {
+                mHandler.removeCallbacks(mStartZenSettings);
+                mHandler.postDelayed(mStartZenSettings, delay);
+            }
+
+            @Override
+            public void onInteraction() {
+                mDialogPanel.resetTimeout();
+            }
+        });
+        mDialogPanel = mPanel;
+    }
+
     private final ContentObserver mObserver = new ContentObserver(mHandler) {
         public void onChange(boolean selfChange, Uri uri) {
             if (SETTING_URI.equals(uri)) {
@@ -71,55 +104,18 @@
         }
     };
 
+    private final Runnable mStartZenSettings = new Runnable() {
+        @Override
+        public void run() {
+            mDialogPanel.postDismiss();
+            final Intent intent = ZenModePanel.ZEN_SETTINGS;
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        }
+    };
+
     /** For now, simply host an unmodified base volume panel in this process. */
     private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
-        private final VolumePanel mDialogPanel;
-        private VolumePanel mPanel;
-
-        public VolumeController(Context context) {
-            mPanel = new VolumePanel(context, null, new ZenModeControllerImpl(mContext, mHandler));
-            final int delay = context.getResources().getInteger(R.integer.feedback_start_delay);
-            mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
-                @Override
-                public void onMoreSettings() {
-                    mHandler.removeCallbacks(mStartZenSettings);
-                    mHandler.postDelayed(mStartZenSettings, delay);
-                }
-
-                @Override
-                public void onInteraction() {
-                    mDialogPanel.resetTimeout();
-                }
-            });
-            mDialogPanel = mPanel;
-        }
-
-        private final Runnable mStartZenSettings = new Runnable() {
-            @Override
-            public void run() {
-                mDialogPanel.postDismiss();
-                final Intent intent = ZenModePanel.ZEN_SETTINGS;
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
-            }
-        };
-
-        @Override
-        public void hasNewRemotePlaybackInfo() throws RemoteException {
-            mPanel.postHasNewRemotePlaybackInfo();
-        }
-
-        @Override
-        public void remoteVolumeChanged(int streamType, int flags)
-                throws RemoteException {
-            mPanel.postRemoteVolumeChanged(streamType, flags);
-        }
-
-        @Override
-        public void remoteSliderVisibility(boolean visible)
-                throws RemoteException {
-            mPanel.postRemoteSliderVisibility(visible);
-        }
 
         @Override
         public void displaySafeVolumeWarning(int flags) throws RemoteException {
@@ -163,4 +159,21 @@
             mPanel = panel == null ? mDialogPanel : panel;
         }
     }
+
+    private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
+
+        @Override
+        public void remoteVolumeChanged(ISessionController binder, int flags)
+                throws RemoteException {
+            MediaController controller = MediaController.fromBinder(binder);
+            mPanel.postRemoteVolumeChanged(controller, flags);
+        }
+
+        @Override
+        public void updateRemoteController(ISessionController session) throws RemoteException {
+            mPanel.postRemoteSliderVisibility(session != null);
+            // TODO stash default session in case the slider can be opened other
+            // than by remoteVolumeChanged.
+        }
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 0c16b78..56a8f7c 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -21,6 +21,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
 
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
@@ -64,6 +65,8 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerInternal;
 import android.view.WindowManagerPolicy.WindowManagerFuncs;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
@@ -94,6 +97,7 @@
     private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
     private static final String GLOBAL_ACTION_KEY_USERS = "users";
     private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
+    private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
 
     private final Context mContext;
     private final WindowManagerFuncs mWindowManagerFuncs;
@@ -279,6 +283,8 @@
                 addUsersToMenu(mItems);
             } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
                 mItems.add(getSettingsAction());
+            } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey) && hasTrustAgents()) {
+                mItems.add(getLockdownAction());
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
@@ -317,6 +323,11 @@
         return dialog;
     }
 
+    private boolean hasTrustAgents() {
+        // TODO: Add implementation.
+        return true;
+    }
+
     private final class PowerAction extends SinglePressAction implements LongPressAction {
         private PowerAction() {
             super(com.android.internal.R.drawable.ic_lock_power_off,
@@ -419,6 +430,32 @@
         };
     }
 
+    private Action getLockdownAction() {
+        return new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock,
+                R.string.global_action_lockdown) {
+
+            @Override
+            public void onPress() {
+                new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
+                try {
+                    WindowManagerGlobal.getWindowManagerService().lockNow(null);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error while trying to lock device.", e);
+                }
+            }
+
+            @Override
+            public boolean showDuringKeyguard() {
+                return true;
+            }
+
+            @Override
+            public boolean showBeforeProvisioning() {
+                return false;
+            }
+        };
+    }
+
     private UserInfo getCurrentUser() {
         try {
             return ActivityManagerNative.getDefault().getCurrentUser();
diff --git a/rs/java/android/renderscript/ScriptIntrinsic.java b/rs/java/android/renderscript/ScriptIntrinsic.java
index 8719e017..4edce84 100644
--- a/rs/java/android/renderscript/ScriptIntrinsic.java
+++ b/rs/java/android/renderscript/ScriptIntrinsic.java
@@ -27,5 +27,8 @@
 public abstract class ScriptIntrinsic extends Script {
     ScriptIntrinsic(long id, RenderScript rs) {
         super(id, rs);
+        if (id == 0) {
+            throw new RSRuntimeException("Loading of ScriptIntrinsic failed.");
+        }
     }
 }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index ae39b05..fb3b117 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1051,7 +1051,7 @@
     jint dimsLen = _env->GetArrayLength(dims) * sizeof(int);
     jint *dimsPtr = _env->GetIntArrayElements(dims, NULL);
     rsScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
-                     (const size_t*) dimsPtr, dimsLen);
+                     (const uint32_t*) dimsPtr, dimsLen);
     _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
     _env->ReleaseIntArrayElements(dims, dimsPtr, JNI_ABORT);
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3bff2ad..9cd5091 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7236,6 +7236,7 @@
         rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
         rti.userId = tr.userId;
         rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
+        rti.lastActiveTime = tr.lastActiveTime;
         return rti;
     }
 
@@ -13959,6 +13960,8 @@
 
         // Make sure that the user who is receiving this broadcast is started.
         // If not, we will just skip it.
+
+
         if (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) {
             if (callingUid != Process.SYSTEM_UID || (intent.getFlags()
                     & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
@@ -13974,8 +13977,8 @@
          */
         int callingAppId = UserHandle.getAppId(callingUid);
         if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
-                || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
-                || callingUid == 0) {
+            || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
+            || callingAppId == Process.NFC_UID || callingUid == 0) {
             // Always okay.
         } else if (callerApp == null || !callerApp.persistent) {
             try {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a301c4b..57dee2e 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -56,6 +56,7 @@
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
     private static final String ATTR_USERID = "user_id";
     private static final String ATTR_TASKTYPE = "task_type";
+    private static final String ATTR_LASTACTIVETIME = "last_active_time";
     private static final String ATTR_LASTDESCRIPTION = "last_description";
     private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
     private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
@@ -136,8 +137,8 @@
     TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
             String _affinity, ComponentName _realActivity, ComponentName _origActivity,
             boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
-            String _lastDescription, ArrayList<ActivityRecord> activities, long lastTimeMoved,
-            boolean neverRelinquishIdentity) {
+            String _lastDescription, ArrayList<ActivityRecord> activities, long _lastActiveTime,
+            long lastTimeMoved, boolean neverRelinquishIdentity) {
         mService = service;
         taskId = _taskId;
         intent = _intent;
@@ -152,10 +153,13 @@
         taskType = _taskType;
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = _userId;
+        lastActiveTime = _lastActiveTime;
         lastDescription = _lastDescription;
         mActivities = activities;
         mLastTimeMoved = lastTimeMoved;
         mNeverRelinquishIdentity = neverRelinquishIdentity;
+        // Recompute the task description for this task
+        updateTaskDescription();
     }
 
     void touchActiveTime() {
@@ -703,6 +707,7 @@
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
         out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
+        out.attribute(null, ATTR_LASTACTIVETIME, String.valueOf(lastActiveTime));
         out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
         out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
         if (lastDescription != null) {
@@ -753,6 +758,7 @@
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
         int userId = 0;
         String lastDescription = null;
+        long lastActiveTime = 0;
         long lastTimeOnTop = 0;
         boolean neverRelinquishIdentity = true;
         int taskId = -1;
@@ -779,6 +785,8 @@
                 userId = Integer.valueOf(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
                 taskType = Integer.valueOf(attrValue);
+            } else if (ATTR_LASTACTIVETIME.equals(attrName)) {
+                lastActiveTime = Long.valueOf(attrValue);
             } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
                 lastDescription = attrValue;
             } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
@@ -818,8 +826,8 @@
 
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
                 affinityIntent, affinity, realActivity, origActivity, rootHasReset,
-                askedCompatMode, taskType, userId, lastDescription, activities, lastTimeOnTop,
-                neverRelinquishIdentity);
+                askedCompatMode, taskType, userId, lastDescription, activities, lastActiveTime,
+                lastTimeOnTop, neverRelinquishIdentity);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 8968da3..ed4ccfc 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -103,6 +103,9 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            if (Build.IS_DEBUGGABLE) {
+                SystemProperties.addChangeCallback(mSystemPropertiesChanged);
+            }
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
@@ -127,6 +130,7 @@
         pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
         pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
         pw.println("mCurrentDreamDozeHardware=" + mCurrentDreamDozeHardware);
+        pw.println("getDozeComponent()=" + getDozeComponent());
         pw.println();
 
         DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
@@ -653,4 +657,18 @@
             }
         }
     }
+
+    private final Runnable mSystemPropertiesChanged = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "System properties changed");
+            synchronized(mLock) {
+                if (mCurrentDreamName != null && mCurrentDreamCanDoze
+                        && !mCurrentDreamName.equals(getDozeComponent())) {
+                    // may have updated the doze component, wake up
+                    stopDreamLocked();
+                }
+            }
+        }
+    };
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1264741..6f1eb8f 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,11 +16,9 @@
 
 package com.android.server.media;
 
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
 import android.media.session.ISessionController;
 import android.media.session.ISessionControllerCallback;
@@ -49,7 +47,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -87,6 +84,12 @@
      */
     private static final int ACTIVE_BUFFER = 30000;
 
+    /**
+     * The amount of time we'll send an assumed volume after the last volume
+     * command before reverting to the last reported volume.
+     */
+    private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000;
+
     private final MessageHandler mHandler;
 
     private final int mOwnerPid;
@@ -122,11 +125,12 @@
 
     // Volume handling fields
     private AudioManager mAudioManager;
-    private int mVolumeType = MediaSession.VOLUME_TYPE_LOCAL;
+    private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
     private int mAudioStream = AudioManager.STREAM_MUSIC;
     private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
     private int mMaxVolume = 0;
     private int mCurrentVolume = 0;
+    private int mOptimisticVolume = -1;
     // End volume handling fields
 
     private boolean mIsActive = false;
@@ -276,7 +280,7 @@
      * @param delta The amount to adjust the volume by.
      */
     public void adjustVolumeBy(int delta, int flags) {
-        if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+        if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
             if (delta == 0) {
                 mAudioManager.adjustStreamVolume(mAudioStream, delta, flags);
             } else {
@@ -298,18 +302,46 @@
                 return;
             }
             mSessionCb.adjustVolumeBy(delta);
+
+            int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
+            mOptimisticVolume = volumeBefore + delta;
+            mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume));
+            mHandler.removeCallbacks(mClearOptimisticVolumeRunnable);
+            mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT);
+            if (volumeBefore != mOptimisticVolume) {
+                pushVolumeUpdate();
+            }
+
+            if (DEBUG) {
+                Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is "
+                        + mMaxVolume);
+            }
         }
     }
 
     public void setVolumeTo(int value, int flags) {
-        if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+        if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
             mAudioManager.setStreamVolume(mAudioStream, value, flags);
         } else {
             if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
                 // Nothing to do. The volume can't be set directly.
                 return;
             }
+            value = Math.max(0, Math.min(value, mMaxVolume));
             mSessionCb.setVolumeTo(value);
+
+            int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
+            mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume));
+            mHandler.removeCallbacks(mClearOptimisticVolumeRunnable);
+            mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT);
+            if (volumeBefore != mOptimisticVolume) {
+                pushVolumeUpdate();
+            }
+
+            if (DEBUG) {
+                Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is "
+                        + mMaxVolume);
+            }
         }
     }
 
@@ -427,6 +459,16 @@
     }
 
     /**
+     * Get the volume we'd like it to be set to. This is only valid for a short
+     * while after a call to adjust or set volume.
+     *
+     * @return The current optimistic volume or -1.
+     */
+    public int getOptimisticVolume() {
+        return mOptimisticVolume;
+    }
+
+    /**
      * @return True if this session is currently connected to a route.
      */
     public boolean isConnected() {
@@ -542,8 +584,7 @@
                     cb.onPlaybackStateChanged(mPlaybackState);
                 } catch (DeadObjectException e) {
                     mControllerCallbacks.remove(i);
-                    Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate. size="
-                            + mControllerCallbacks.size() + " cb=" + cb, e);
+                    Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate.", e);
                 } catch (RemoteException e) {
                     Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e);
                 }
@@ -561,10 +602,29 @@
                 try {
                     cb.onMetadataChanged(mMetadata);
                 } catch (DeadObjectException e) {
-                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate. " + cb, e);
+                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate. ", e);
                     mControllerCallbacks.remove(i);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "unexpected exception in pushMetadataUpdate. " + cb, e);
+                    Log.w(TAG, "unexpected exception in pushMetadataUpdate. ", e);
+                }
+            }
+        }
+    }
+
+    private void pushVolumeUpdate() {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                return;
+            }
+            ParcelableVolumeInfo info = mController.getVolumeAttributes();
+            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+                ISessionControllerCallback cb = mControllerCallbacks.get(i);
+                try {
+                    cb.onVolumeInfoChanged(info);
+                } catch (DeadObjectException e) {
+                    Log.w(TAG, "Removing dead callback in pushVolumeUpdate. ", e);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Unexpected exception in pushVolumeUpdate. ", e);
                 }
             }
         }
@@ -680,6 +740,17 @@
         }
     };
 
+    private final Runnable mClearOptimisticVolumeRunnable = new Runnable() {
+        @Override
+        public void run() {
+            boolean needUpdate = (mOptimisticVolume != mCurrentVolume);
+            mOptimisticVolume = -1;
+            if (needUpdate) {
+                pushVolumeUpdate();
+            }
+        }
+    };
+
     private final class SessionStub extends ISession.Stub {
         @Override
         public void destroy() {
@@ -785,12 +856,14 @@
         @Override
         public void setCurrentVolume(int volume) {
             mCurrentVolume = volume;
+            mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
         }
 
         @Override
         public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
+            boolean typeChanged = type != mVolumeType;
             switch(type) {
-                case MediaSession.VOLUME_TYPE_LOCAL:
+                case MediaSession.PLAYBACK_TYPE_LOCAL:
                     mVolumeType = type;
                     int audioStream = arg1;
                     if (isValidStream(audioStream)) {
@@ -800,7 +873,7 @@
                         mAudioStream = AudioManager.STREAM_MUSIC;
                     }
                     break;
-                case MediaSession.VOLUME_TYPE_REMOTE:
+                case MediaSession.PLAYBACK_TYPE_REMOTE:
                     mVolumeType = type;
                     mVolumeControlType = arg1;
                     mMaxVolume = arg2;
@@ -809,6 +882,9 @@
                     throw new IllegalArgumentException("Volume handling type " + type
                             + " not recognized.");
             }
+            if (typeChanged) {
+                mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
+            }
         }
 
         private boolean isValidStream(int stream) {
@@ -1027,10 +1103,11 @@
                 int type;
                 int max;
                 int current;
-                if (mVolumeType == MediaSession.VOLUME_TYPE_REMOTE) {
+                if (mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE) {
                     type = mVolumeControlType;
                     max = mMaxVolume;
-                    current = mCurrentVolume;
+                    current = mOptimisticVolume != -1 ? mOptimisticVolume
+                            : mCurrentVolume;
                 } else {
                     type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
                     max = mAudioManager.getStreamMaxVolume(mAudioStream);
@@ -1130,6 +1207,7 @@
         private static final int MSG_UPDATE_ROUTE_FILTERS = 5;
         private static final int MSG_SEND_COMMAND = 6;
         private static final int MSG_UPDATE_SESSION_STATE = 7;
+        private static final int MSG_UPDATE_VOLUME = 8;
 
         public MessageHandler(Looper looper) {
             super(looper);
@@ -1157,6 +1235,9 @@
                 case MSG_UPDATE_SESSION_STATE:
                     // TODO add session state
                     break;
+                case MSG_UPDATE_VOLUME:
+                    pushVolumeUpdate();
+                    break;
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c0b7d68..fe68a86 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -27,8 +27,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.media.AudioManager;
 import android.media.IAudioService;
+import android.media.IRemoteVolumeController;
 import android.media.routeprovider.RouteRequest;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ISession;
@@ -98,8 +98,9 @@
     // session so we drop late callbacks properly.
     private int mShowRoutesRequestId = 0;
 
-    // TODO refactor to have per user state for providers. See
-    // MediaRouterService for an example
+    // Used to notify system UI when remote volume was changed. TODO find a
+    // better way to handle this.
+    private IRemoteVolumeController mRvc;
 
     public MediaSessionService(Context context) {
         super(context);
@@ -225,6 +226,16 @@
         }
     }
 
+    public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
+        synchronized (mLock) {
+            if (!mAllSessions.contains(record)) {
+                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
+                return;
+            }
+            pushRemoteVolumeUpdateLocked(record.getUserId());
+        }
+    }
+
     @Override
     public void onStartUser(int userHandle) {
         updateUser();
@@ -367,6 +378,13 @@
         }
     }
 
+    private void enforceStatusBarPermission(String action, int pid, int uid) {
+        if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+                pid, uid) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Only system ui may " + action);
+        }
+    }
+
     /**
      * This checks if the component is an enabled notification listener for the
      * specified user. Enabled components may only operate on behalf of the user
@@ -497,6 +515,7 @@
             for (int i = 0; i < size; i++) {
                 tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
             }
+            pushRemoteVolumeUpdateLocked(userId);
             for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
                 SessionsListenerRecord record = mSessionsListeners.get(i);
                 if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
@@ -512,6 +531,17 @@
         }
     }
 
+    private void pushRemoteVolumeUpdateLocked(int userId) {
+        if (mRvc != null) {
+            try {
+                MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
+                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
+            }
+        }
+    }
+
     private void persistMediaButtonReceiverLocked(MediaSessionRecord record) {
         ComponentName receiver = record.getMediaButtonReceiver();
         if (receiver != null) {
@@ -844,6 +874,19 @@
         }
 
         @Override
+        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                enforceStatusBarPermission("listen for volume changes", pid, uid);
+                mRvc = rvc;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -929,6 +972,13 @@
                 }
             } else {
                 session.adjustVolumeBy(delta, flags);
+                if (mRvc != null) {
+                    try {
+                        mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
+                    } catch (Exception e) {
+                        Log.wtf(TAG, "Error sending volume change to system UI.", e);
+                    }
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 144ccfa..e26a2eb 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -203,6 +203,19 @@
         return null;
     }
 
+    public MediaSessionRecord getDefaultRemoteSession(int userId) {
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+
+        int size = records.size();
+        for (int i = 0; i < size; i++) {
+            MediaSessionRecord record = records.get(i);
+            if (record.getPlaybackType() == MediaSession.PLAYBACK_TYPE_REMOTE) {
+                return record;
+            }
+        }
+        return null;
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
                 UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 32546df..1aec569 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -88,6 +88,7 @@
     private static final int MSG_UNREGISTER_LISTENER = 2;
     private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
     private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
+    private static final int MSG_REQUIRE_CREDENTIAL_ENTRY = 5;
 
     private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
     private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
@@ -314,6 +315,17 @@
         }
     }
 
+
+    private void requireCredentialEntry(int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            mUserHasAuthenticatedSinceBoot.clear();
+            updateTrustAll();
+        } else {
+            mUserHasAuthenticatedSinceBoot.put(userId, false);
+            updateTrust(userId);
+        }
+    }
+
     // Listeners
 
     private void addListener(ITrustListener listener) {
@@ -367,6 +379,17 @@
         }
 
         @Override
+        public void reportRequireCredentialEntry(int userId) throws RemoteException {
+            enforceReportPermission();
+            if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
+                mHandler.obtainMessage(MSG_REQUIRE_CREDENTIAL_ENTRY, userId, 0).sendToTarget();
+            } else {
+                throw new IllegalArgumentException(
+                        "userId must be an explicit user id or USER_ALL");
+            }
+        }
+
+        @Override
         public void registerTrustListener(ITrustListener trustListener) throws RemoteException {
             enforceListenerPermission();
             mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget();
@@ -379,8 +402,8 @@
         }
 
         private void enforceReportPermission() {
-            mContext.enforceCallingPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
-                    "reporting trust events");
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
         }
 
         private void enforceListenerPermission() {
@@ -460,6 +483,9 @@
                 case MSG_ENABLED_AGENTS_CHANGED:
                     refreshAgentList();
                     break;
+                case MSG_REQUIRE_CREDENTIAL_ENTRY:
+                    requireCredentialEntry(msg.arg1);
+                    break;
             }
         }
     };
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9c38bbc..893a891 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2255,9 +2255,12 @@
         try {
             LockPatternUtils utils = new LockPatternUtils(mContext);
             utils.saveLockPassword(password, quality, false, userHandle);
+            boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
+            if (requireEntry) {
+                utils.requireCredentialEntry(UserHandle.USER_ALL);
+            }
             synchronized (this) {
-                int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
-                        != 0 ? callingUid : -1;
+                int newOwner = requireEntry ? callingUid : -1;
                 if (policy.mPasswordOwner != newOwner) {
                     policy.mPasswordOwner = newOwner;
                     saveSettingsLocked(userHandle);
@@ -2370,6 +2373,7 @@
             getIPowerManager().goToSleep(SystemClock.uptimeMillis(),
                     PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
             // Ensure the device is locked
+            new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
             getWindowManager().lockNow(null);
         } catch (RemoteException e) {
         } finally {
diff --git a/telecomm/java/android/telecomm/CallVideoProvider.java b/telecomm/java/android/telecomm/CallVideoProvider.java
new file mode 100644
index 0000000..6126fca
--- /dev/null
+++ b/telecomm/java/android/telecomm/CallVideoProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package android.telecomm;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+
+import com.android.internal.telecomm.ICallVideoProvider;
+
+/** @hide */
+public abstract class CallVideoProvider {
+    private static final int MSG_SET_CAMERA = 1;
+
+    /**
+     * Default handler used to consolidate binder method calls onto a single thread.
+     */
+    private final class CallVideoProviderHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_CAMERA:
+                    setCamera((String) msg.obj);
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Default ICallVideoProvider implementation.
+     */
+    private final class CallVideoProviderBinder extends ICallVideoProvider.Stub {
+        public void setCamera(String cameraId) {
+            mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
+        }
+    }
+
+    private final CallVideoProviderHandler mMessageHandler = new CallVideoProviderHandler();
+    private final CallVideoProviderBinder mBinder;
+
+    protected CallVideoProvider() {
+        mBinder = new CallVideoProviderBinder();
+    }
+
+    /**
+     * Sets the camera to be used for video recording in a video call.
+     *
+     * @param cameraId The id of the camera.
+     */
+    public abstract void setCamera(String cameraId);
+}
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index a97e7e4..a0abc28 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -16,7 +16,12 @@
 
 package android.telecomm;
 
+import android.annotation.SystemApi;
+import android.content.ComponentName;
 import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
 
 import com.android.internal.telecomm.ITelecommService;
 
@@ -25,11 +30,14 @@
  */
 public class TelecommManager {
     private static final String TAG = "TelecommManager";
+    private static final String TELECOMM_SERVICE_NAME = "telecomm";
 
     private final Context mContext;
     private final ITelecommService mService;
 
-    /** @hide */
+    /**
+     * @hide
+     */
     public TelecommManager(Context context, ITelecommService service) {
         Context appContext = context.getApplicationContext();
         if (appContext != null) {
@@ -41,8 +49,27 @@
         mService = service;
     }
 
-    /** {@hide} */
+    /**
+     * @hide
+     */
     public static TelecommManager from(Context context) {
         return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
     }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public ComponentName getDefaultPhoneApp() {
+        try {
+            return getTelecommService().getDefaultPhoneApp();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
+        }
+        return null;
+    }
+
+    private ITelecommService getTelecommService() {
+        return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+    }
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl b/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
new file mode 100644
index 0000000..c116dcb
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.internal.telecomm;
+
+/**
+ * Internal remote interface for a call video provider.
+ * @see android.telecomm.CallVideoProvider
+ * @hide
+ */
+oneway interface ICallVideoProvider {
+    void setCamera(String cameraId);
+}
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index c758c6d..2ae5768 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.telecomm;
 
+import android.content.ComponentName;
 import android.telecomm.Subscription;
 
 /**
@@ -55,4 +56,9 @@
      * Sets a given Subscription as the system default.
      */
     void setSystemDefault(in Subscription subscription);
+
+    /**
+     * Returns the component name of the default phone application.
+     */
+    ComponentName getDefaultPhoneApp();
 }
diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
index 1c9c40d..ee2e895 100644
--- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
+++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
@@ -26,6 +26,7 @@
 interface IWifiP2pManager
 {
     Messenger getMessenger();
+    Messenger getP2pStateMachineMessenger();
     void setMiracastMode(int mode);
 }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 3ed2406..6409450 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -276,6 +276,13 @@
     public static final String WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION =
         "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED";
 
+    /**
+     * The lookup key for a handover message returned by the WifiP2pService.
+     * @hide
+     */
+    public static final String EXTRA_HANDOVER_MESSAGE =
+            "android.net.wifi.p2p.EXTRA_HANDOVER_MESSAGE";
+
     IWifiP2pManager mService;
 
     private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
@@ -446,6 +453,21 @@
     /** @hide */
     public static final int SET_CHANNEL_SUCCEEDED                   = BASE + 73;
 
+    /** @hide */
+    public static final int GET_HANDOVER_REQUEST                    = BASE + 75;
+    /** @hide */
+    public static final int GET_HANDOVER_SELECT                     = BASE + 76;
+    /** @hide */
+    public static final int RESPONSE_GET_HANDOVER_MESSAGE           = BASE + 77;
+    /** @hide */
+    public static final int INITIATOR_REPORT_NFC_HANDOVER           = BASE + 78;
+    /** @hide */
+    public static final int RESPONDER_REPORT_NFC_HANDOVER           = BASE + 79;
+    /** @hide */
+    public static final int REPORT_NFC_HANDOVER_SUCCEEDED           = BASE + 80;
+    /** @hide */
+    public static final int REPORT_NFC_HANDOVER_FAILED              = BASE + 81;
+
 
     /**
      * Create a new WifiP2pManager instance. Applications use
@@ -627,6 +649,14 @@
     }
 
     /**
+     * Interface for callback invocation when Handover Request or Select Message is available
+     * @hide
+     */
+    public interface HandoverMessageListener {
+        public void onHandoverMessageAvailable(String handoverMessage);
+    }
+
+    /**
      * A channel that connects the application to the Wifi p2p framework.
      * Most p2p operations require a Channel as an argument. An instance of Channel is obtained
      * by doing a call on {@link #initialize}
@@ -687,6 +717,7 @@
                     case START_LISTEN_FAILED:
                     case STOP_LISTEN_FAILED:
                     case SET_CHANNEL_FAILED:
+                    case REPORT_NFC_HANDOVER_FAILED:
                         if (listener != null) {
                             ((ActionListener) listener).onFailure(message.arg1);
                         }
@@ -712,6 +743,7 @@
                     case START_LISTEN_SUCCEEDED:
                     case STOP_LISTEN_SUCCEEDED:
                     case SET_CHANNEL_SUCCEEDED:
+                    case REPORT_NFC_HANDOVER_SUCCEEDED:
                         if (listener != null) {
                             ((ActionListener) listener).onSuccess();
                         }
@@ -745,7 +777,17 @@
                                 onPersistentGroupInfoAvailable(groups);
                         }
                         break;
-                   default:
+                    case RESPONSE_GET_HANDOVER_MESSAGE:
+                        Bundle handoverBundle = (Bundle) message.obj;
+                        if (listener != null) {
+                            String handoverMessage = handoverBundle != null
+                                    ? handoverBundle.getString(EXTRA_HANDOVER_MESSAGE)
+                                    : null;
+                            ((HandoverMessageListener) listener)
+                                    .onHandoverMessageAvailable(handoverMessage);
+                        }
+                        break;
+                    default:
                         Log.d(TAG, "Ignored " + message);
                         break;
                 }
@@ -841,7 +883,20 @@
      * @return Channel instance that is necessary for performing any further p2p operations
      */
     public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
-        Messenger messenger = getMessenger();
+        return initalizeChannel(srcContext, srcLooper, listener, getMessenger());
+    }
+
+    /**
+     * Registers the application with the Wi-Fi framework. Enables system-only functionality.
+     * @hide
+     */
+    public Channel initializeInternal(Context srcContext, Looper srcLooper,
+                                      ChannelListener listener) {
+        return initalizeChannel(srcContext, srcLooper, listener, getP2pStateMachineMessenger());
+    }
+
+    private Channel initalizeChannel(Context srcContext, Looper srcLooper, ChannelListener listener,
+                                     Messenger messenger) {
         if (messenger == null) return null;
 
         Channel c = new Channel(srcContext, srcLooper, listener);
@@ -1327,4 +1382,62 @@
         }
     }
 
+    /**
+     * Get a reference to P2pStateMachine handler. This is used to establish
+     * a priveleged AsyncChannel communication with WifiP2pService.
+     *
+     * @return Messenger pointing to the WifiP2pService handler
+     * @hide
+     */
+    public Messenger getP2pStateMachineMessenger() {
+        try {
+            return mService.getP2pStateMachineMessenger();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Get a handover request message for use in WFA NFC Handover transfer.
+     * @hide
+     */
+    public void getNfcHandoverRequest(Channel c, HandoverMessageListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(GET_HANDOVER_REQUEST, 0, c.putListener(listener));
+    }
+
+
+    /**
+     * Get a handover select message for use in WFA NFC Handover transfer.
+     * @hide
+     */
+    public void getNfcHandoverSelect(Channel c, HandoverMessageListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(GET_HANDOVER_SELECT, 0, c.putListener(listener));
+    }
+
+    /**
+     * @hide
+     */
+    public void initiatorReportNfcHandover(Channel c, String handoverSelect,
+                                              ActionListener listener) {
+        checkChannel(c);
+        Bundle bundle = new Bundle();
+        bundle.putString(EXTRA_HANDOVER_MESSAGE, handoverSelect);
+        c.mAsyncChannel.sendMessage(INITIATOR_REPORT_NFC_HANDOVER, 0,
+                c.putListener(listener), bundle);
+    }
+
+
+    /**
+     * @hide
+     */
+    public void responderReportNfcHandover(Channel c, String handoverRequest,
+                                              ActionListener listener) {
+        checkChannel(c);
+        Bundle bundle = new Bundle();
+        bundle.putString(EXTRA_HANDOVER_MESSAGE, handoverRequest);
+        c.mAsyncChannel.sendMessage(RESPONDER_REPORT_NFC_HANDOVER, 0,
+                c.putListener(listener), bundle);
+    }
 }