Merge "Original DnsSd TxtRecord file"
diff --git a/api/current.txt b/api/current.txt
index 95007b5..10377fd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12899,6 +12899,7 @@
     method public byte[] getHistoricalBytes();
     method public int getMaxTransceiveLength();
     method public int getTimeout();
+    method public boolean isExtendedLengthApduSupported();
     method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 2223255..3ac1dcc 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -45,4 +45,5 @@
     void resetTimeouts();
     boolean canMakeReadOnly(int ndefType);
     int getMaxTransceiveLength(int technology);
+    boolean getExtendedLengthApdusSupported();
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b7a7bd5..d78e06c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -580,7 +580,18 @@
      * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
      * then NDEF push will be completely disabled for the specified activity(s).
      * This also disables any default NDEF message the Android OS would have
-     * otherwise sent on your behalf.
+     * otherwise sent on your behalf for those activity(s).
+     *
+     * <p>If you want to prevent the Android OS from sending default NDEF
+     * messages completely (for all activities), you can include a
+     * <code><meta-data></code> element inside the <code><application></code>
+     * element of your AndroidManifest.xml file, like this:
+     * <pre>{@code
+     *  <application ...>
+     *      <meta-data android:name="android.nfc.disable_beam_default"
+     *          android:value="true" />
+     *  </application>
+     * }</pre>
      *
      * <p>The API allows for multiple activities to be specified at a time,
      * but it is strongly recommended to just register one at a time,
@@ -664,7 +675,18 @@
      * and/or {@link #setNdefPushMessageCallback} is called with a null callback,
      * then NDEF push will be completely disabled for the specified activity(s).
      * This also disables any default NDEF message the Android OS would have
-     * otherwise sent on your behalf.
+     * otherwise sent on your behalf for those activity(s).
+     *
+     * <p>If you want to prevent the Android OS from sending default NDEF
+     * messages completely (for all activities), you can include a
+     * <code><meta-data></code> element inside the <code><application></code>
+     * element of your AndroidManifest.xml file, like this:
+     * <pre>{@code
+     *  <application ...>
+     *      <meta-data android:name="android.nfc.disable_beam_default"
+     *          android:value="true" />
+     *  </application>
+     * }</pre>
      *
      * <p>The API allows for multiple activities to be specified at a time,
      * but it is strongly recommended to just register one at a time,
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 1859877..089b159 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -179,4 +179,27 @@
     public int getMaxTransceiveLength() {
         return getMaxTransceiveLengthInternal();
     }
+
+    /**
+     * <p>Standard APDUs have a 1-byte length field, allowing a maximum of
+     * 255 payload bytes, which results in a maximum APDU length of 261 bytes.
+     *
+     * <p>Extended length APDUs have a 3-byte length field, allowing 65535
+     * payload bytes.
+     *
+     * <p>Some NFC adapters, like the one used in the Nexus S and the Galaxy Nexus
+     * do not support extended length APDUs. They are expected to be well-supported
+     * in the future though. Use this method to check for extended length APDU
+     * support.
+     *
+     * @return whether the NFC adapter on this device supports extended length APDUs.
+     */
+    public boolean isExtendedLengthApduSupported() {
+        try {
+            return mTag.getTagService().getExtendedLengthApdusSupported();
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 74a376d..8df4339 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -497,27 +497,30 @@
      * @see #onCreateView(ViewGroup)
      */
     protected void onBindView(View view) {
-        TextView textView = (TextView) view.findViewById(com.android.internal.R.id.title); 
-        if (textView != null) {
-            textView.setText(getTitle());
-        }
-        
-        textView = (TextView) view.findViewById(com.android.internal.R.id.summary);
-        if (textView != null) {
-            final CharSequence summary = getSummary();
-            if (!TextUtils.isEmpty(summary)) {
-                if (textView.getVisibility() != View.VISIBLE) {
-                    textView.setVisibility(View.VISIBLE);
-                }
-                
-                textView.setText(getSummary());
+        final TextView titleView = (TextView) view.findViewById(
+                com.android.internal.R.id.title);
+        if (titleView != null) {
+            final CharSequence title = getTitle();
+            if (!TextUtils.isEmpty(title)) {
+                titleView.setText(title);
+                titleView.setVisibility(View.VISIBLE);
             } else {
-                if (textView.getVisibility() != View.GONE) {
-                    textView.setVisibility(View.GONE);
-                }
+                titleView.setVisibility(View.GONE);
             }
         }
-        
+
+        final TextView summaryView = (TextView) view.findViewById(
+                com.android.internal.R.id.summary);
+        if (summaryView != null) {
+            final CharSequence summary = getSummary();
+            if (!TextUtils.isEmpty(summary)) {
+                summaryView.setText(summary);
+                summaryView.setVisibility(View.VISIBLE);
+            } else {
+                summaryView.setVisibility(View.GONE);
+            }
+        }
+
         ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
         if (imageView != null) {
             if (mIconResId != 0 || mIcon != null) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 9ef2621..b8c692a 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -87,7 +87,7 @@
     /**
      * System property used to enable or disable hardware rendering profiling.
      * The default value of this property is assumed to be false.
-     * 
+     *
      * When profiling is enabled, the adb shell dumpsys gfxinfo command will
      * output extra information about the time taken to execute by the last
      * frames.
@@ -99,6 +99,20 @@
     static final String PROFILE_PROPERTY = "hwui.profile";
 
     /**
+     * System property used to specify the number of frames to be used
+     * when doing hardware rendering profiling.
+     * The default value of this property is #PROFILE_MAX_FRAMES.
+     *
+     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+     * output extra information about the time taken to execute by the last
+     * frames.
+     *
+     * Possible values:
+     * "60", to set the limit of frames to 60
+     */
+    static final String PROFILE_MAXFRAMES_PROPERTY = "hwui.profile.maxframes";
+
+    /**
      * System property used to debug EGL configuration choice.
      * 
      * Possible values:
@@ -134,7 +148,7 @@
     /**
      * Number of frames to profile.
      */
-    private static final int PROFILE_MAX_FRAMES = 120;
+    private static final int PROFILE_MAX_FRAMES = 64;
 
     /**
      * Number of floats per profiled frame.
@@ -579,7 +593,13 @@
             }
 
             if (mProfileEnabled) {
-                mProfileData = new float[PROFILE_MAX_FRAMES * PROFILE_FRAME_DATA_COUNT];
+                property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY,
+                        Integer.toString(PROFILE_MAX_FRAMES));
+                int maxProfileFrames = Integer.valueOf(property);
+                mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
+                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+                }
             } else {
                 mProfileData = null;
             }
@@ -596,9 +616,14 @@
             if (mProfileEnabled) {
                 pw.printf("\n\tDraw\tProcess\tExecute\n");
                 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+                    if (mProfileData[i] < 0) {
+                        break;
+                    }
                     pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
                             mProfileData[i + 2]);
+                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
                 }
+                mProfileCurrentFrame = mProfileData.length;
             }
         }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 880dc34..cbff58c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1609,7 +1609,6 @@
         private boolean mCancelled;
 
         public void run() {
-            Log.d("GILLES", "blinking !!!");
             if (mCancelled) {
                 return;
             }
diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml
index 5b06460..93843fd 100644
--- a/core/res/res/layout/notification_template_base.xml
+++ b/core/res/res/layout/notification_template_base.xml
@@ -28,7 +28,7 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
+        android:layout_gravity="fill_vertical"
         android:layout_marginLeft="@dimen/notification_large_icon_width"
         android:minHeight="@dimen/notification_large_icon_height"
         android:orientation="vertical"
@@ -36,6 +36,7 @@
         android:paddingRight="12dp"
         android:paddingTop="4dp"
         android:paddingBottom="4dp"
+        android:gravity="center_vertical"
         >
         <LinearLayout
             android:id="@+id/line1"
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
index dc3ded1..b188538 100644
--- a/docs/html/design/building-blocks/progress.jd
+++ b/docs/html/design/building-blocks/progress.jd
@@ -42,9 +42,9 @@
       <li class="value-1"><h4>Activity bar (shown with the Holo Dark theme)</h4>
         <p>
 
-An indeterminate activity bar is used at the start of an application download because Google Play hasn't
-been able to contact the server yet, and it's not possible to determine how long it will take for
-the download to begin.
+An indeterminate activity bar is used at the start of an application download because the Play Store
+app hasn't been able to contact the server yet, and it's not possible to determine how long it will
+take for the download to begin.
 
         </p>
       </li>
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
index 2c854d3..19ed1c3 100644
--- a/docs/html/design/building-blocks/tabs.jd
+++ b/docs/html/design/building-blocks/tabs.jd
@@ -25,7 +25,7 @@
       <source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
     </video>
     <div class="figure-caption">
-      Scrolling tabs in Google Play.
+      Scrolling tabs in the Play Store app.
       <div class="video-instructions">&nbsp;</div>
     </div>
 
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 19b58d9..6dd8d61 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -35,6 +35,7 @@
       <li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/selection.html">Selection</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/notifications.html">Notifications</a></li>
+      <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
     </ul>
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 618c44b..67dfd79 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -39,7 +39,7 @@
 <p>
   <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe&reg; Fireworks&reg; PNG Stencil</a>
   <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni&reg; OmniGraffle&reg; Stencil</a>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120229.zip">Adobe&reg; Photoshop&reg; Sources</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120302.zip">Adobe&reg; Photoshop&reg; Sources</a>
 </p>
 
   </div>
diff --git a/docs/html/design/get-started/principles.jd b/docs/html/design/get-started/principles.jd
index 8f5b446..0b7147b 100644
--- a/docs/html/design/get-started/principles.jd
+++ b/docs/html/design/get-started/principles.jd
@@ -10,7 +10,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Delight me in surprising ways</h4>
+<h4 id="delight-me">Delight me in surprising ways</h4>
 <p>A beautiful surface, a carefully-placed animation, or a well-timed sound effect is a joy to
 experience. Subtle effects contribute to a feeling of effortlessness and a sense that a powerful
 force is at hand.</p>
@@ -28,7 +28,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Real objects are more fun than buttons and menus</h4>
+<h4 id="real-objects-more-fun">Real objects are more fun than buttons and menus</h4>
 <p>Allow people to directly touch and manipulate objects in your app. It reduces the cognitive effort
 needed to perform a task while making it more emotionally satisfying.</p>
 
@@ -45,7 +45,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Let me make it mine</h4>
+<h4 id="make-it-mine">Let me make it mine</h4>
 <p>People love to add personal touches because it helps them feel at home and in control. Provide
 sensible, beautiful defaults, but also consider fun, optional customizations that don't hinder
 primary tasks.</p>
@@ -63,7 +63,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Get to know me</h4>
+<h4 id="get-to-know-me">Get to know me</h4>
 <p>Learn peoples' preferences over time. Rather than asking them to make the same choices over and
 over, place previous choices within easy reach.</p>
 
@@ -80,7 +80,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Keep it brief</h4>
+<h4 id="keep-it-brief">Keep it brief</h4>
 <p>Use short phrases with simple words. People are likely to skip sentences if they're long.</p>
 
   </div>
@@ -96,7 +96,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Pictures are faster than words</h4>
+<h4 id="pictures-faster-than-words">Pictures are faster than words</h4>
 <p>Consider using pictures to explain ideas. They get people's attention and can be much more efficient
 than words.</p>
 
@@ -113,7 +113,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Decide for me but let me have the final say</h4>
+<h4 id="decide-for-me">Decide for me but let me have the final say</h4>
 <p>Take your best guess and act rather than asking first. Too many choices and decisions make people
 unhappy. Just in case you get it wrong, allow for 'undo'.</p>
 
@@ -130,7 +130,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Only show what I need when I need it</h4>
+<h4 id="only-show-when-i-need-it">Only show what I need when I need it</h4>
 <p>People get overwhelmed when they see too much at once. Break tasks and information into small,
 digestible chunks. Hide options that aren't essential at the moment, and teach people as they go.</p>
 
@@ -147,7 +147,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>I should always know where I am</h4>
+<h4 id="always-know-where-i-am">I should always know where I am</h4>
 <p>Give people confidence that they know their way around. Make places in your app look distinct and
 use transitions to show relationships among screens. Provide feedback on tasks in progress.</p>
 
@@ -164,7 +164,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Never lose my stuff</h4>
+<h4 id="never-lose-my-stuff">Never lose my stuff</h4>
 <p>Save what people took time to create and let them access it from anywhere. Remember settings,
 personal touches, and creations across phones, tablets, and computers. It makes upgrading the
 easiest thing in the world.</p>
@@ -182,7 +182,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>If it looks the same, it should act the same</h4>
+<h4 id="looks-same-should-act-same">If it looks the same, it should act the same</h4>
 <p>Help people discern functional differences by making them visually distinct rather than subtle.
 Avoid modes, which are places that look similar but act differently on the same input.</p>
 
@@ -199,7 +199,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Only interrupt me if it's important</h4>
+<h4 id="interrupt-only-if-important">Only interrupt me if it's important</h4>
 <p>Like a good personal assistant, shield people from unimportant minutiae. People want to stay
 focused, and unless it's critical and time-sensitive, an interruption can be taxing and frustrating.</p>
 
@@ -216,7 +216,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Give me tricks that work everywhere</h4>
+<h4 id="give-me-tricks">Give me tricks that work everywhere</h4>
 <p>People feel great when they figure things out for themselves. Make your app easier to learn by
 leveraging visual patterns and muscle memory from other Android apps. For example, the swipe gesture
 may be a good navigational shortcut.</p>
@@ -234,7 +234,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>It's not my fault</h4>
+<h4 id="its-not-my-fault">It's not my fault</h4>
 <p>Be gentle in how you prompt people to make corrections. They want to feel smart when they use your
 app. If something goes wrong, give clear recovery instructions but spare them the technical details.
 If you can fix it behind the scenes, even better.</p>
@@ -252,7 +252,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Sprinkle encouragement</h4>
+<h4 id="sprinkle-encouragement">Sprinkle encouragement</h4>
 <p>Break complex tasks into smaller steps that can be easily accomplished. Give feedback on actions,
 even if it's just a subtle glow.</p>
 
@@ -269,7 +269,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Do the heavy lifting for me</h4>
+<h4 id="do-heavy-lifting-for-me">Do the heavy lifting for me</h4>
 <p>Make novices feel like experts by enabling them to do things they never thought they could. For
 example, shortcuts that combine multiple photo effects can make amateur photographs look amazing in
 only a few steps.</p>
@@ -287,7 +287,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
 
-<h4>Make important things fast</h4>
+<h4 id="make-important-things-fast">Make important things fast</h4>
 <p>Not all actions are equal. Decide what's most important in your app and make it easy to find and
 fast to use, like the shutter button in a camera, or the pause button in a music player.</p>
 
diff --git a/docs/html/design/media/app_structure_market.png b/docs/html/design/media/app_structure_market.png
index 3b0b786..5aa595e 100644
--- a/docs/html/design/media/app_structure_market.png
+++ b/docs/html/design/media/app_structure_market.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_music_lndscp.png b/docs/html/design/media/app_structure_music_lndscp.png
index 0dd400c..67354de 100644
--- a/docs/html/design/media/app_structure_music_lndscp.png
+++ b/docs/html/design/media/app_structure_music_lndscp.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_scrolltabs.png b/docs/html/design/media/app_structure_scrolltabs.png
index ef4fca4..ea742c2 100644
--- a/docs/html/design/media/app_structure_scrolltabs.png
+++ b/docs/html/design/media/app_structure_scrolltabs.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_shortcut_on_item.png b/docs/html/design/media/app_structure_shortcut_on_item.png
index 3874e1d4..1341f1f 100644
--- a/docs/html/design/media/app_structure_shortcut_on_item.png
+++ b/docs/html/design/media/app_structure_shortcut_on_item.png
Binary files differ
diff --git a/docs/html/design/media/iconography_launcher_example.png b/docs/html/design/media/iconography_launcher_example.png
index a5db53e..0cce7ef9 100644
--- a/docs/html/design/media/iconography_launcher_example.png
+++ b/docs/html/design/media/iconography_launcher_example.png
Binary files differ
diff --git a/docs/html/design/media/iconography_overview.png b/docs/html/design/media/iconography_overview.png
index 688c1b5..56cd409 100644
--- a/docs/html/design/media/iconography_overview.png
+++ b/docs/html/design/media/iconography_overview.png
Binary files differ
diff --git a/docs/html/design/media/migrating_intents.png b/docs/html/design/media/migrating_intents.png
new file mode 100644
index 0000000..65fc1a5
--- /dev/null
+++ b/docs/html/design/media/migrating_intents.png
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png b/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
deleted file mode 100644
index bab6aca..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
deleted file mode 100644
index bdccc2f..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
deleted file mode 100644
index 5e0135b..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_back.png b/docs/html/design/media/navigation_between_apps_back.png
new file mode 100755
index 0000000..ded5d0a
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_back.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_inward.png b/docs/html/design/media/navigation_between_apps_inward.png
new file mode 100755
index 0000000..1f5e401
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_inward.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_up.png b/docs/html/design/media/navigation_between_apps_up.png
new file mode 100755
index 0000000..f192c88
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_up.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market1.png b/docs/html/design/media/navigation_between_siblings_market1.png
old mode 100644
new mode 100755
index c3148f8..8f2b3dc
--- a/docs/html/design/media/navigation_between_siblings_market1.png
+++ b/docs/html/design/media/navigation_between_siblings_market1.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market2.png b/docs/html/design/media/navigation_between_siblings_market2.png
old mode 100644
new mode 100755
index 208be47..33b654c
--- a/docs/html/design/media/navigation_between_siblings_market2.png
+++ b/docs/html/design/media/navigation_between_siblings_market2.png
Binary files differ
diff --git a/docs/html/design/media/navigation_from_outside_up.png b/docs/html/design/media/navigation_from_outside_up.png
deleted file mode 100644
index eaa3cdb..0000000
--- a/docs/html/design/media/navigation_from_outside_up.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_indirect_notification.png b/docs/html/design/media/navigation_indirect_notification.png
new file mode 100644
index 0000000..6f99267
--- /dev/null
+++ b/docs/html/design/media/navigation_indirect_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_popup_notification.png b/docs/html/design/media/navigation_popup_notification.png
new file mode 100644
index 0000000..a0a3ee7
--- /dev/null
+++ b/docs/html/design/media/navigation_popup_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_up_vs_back_gmail.png b/docs/html/design/media/navigation_up_vs_back_gmail.png
index 71e6484..ff7adfe 100644
--- a/docs/html/design/media/navigation_up_vs_back_gmail.png
+++ b/docs/html/design/media/navigation_up_vs_back_gmail.png
Binary files differ
diff --git a/docs/html/design/media/navigation_with_back_and_up.png b/docs/html/design/media/navigation_with_back_and_up.png
index 4fb6dce..5440220 100644
--- a/docs/html/design/media/navigation_with_back_and_up.png
+++ b/docs/html/design/media/navigation_with_back_and_up.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity.png b/docs/html/design/media/progress_activity.png
index 51210b4..32cf1f5 100644
--- a/docs/html/design/media/progress_activity.png
+++ b/docs/html/design/media/progress_activity.png
Binary files differ
diff --git a/docs/html/design/media/progress_download.png b/docs/html/design/media/progress_download.png
index f567f74..ab6bf58 100644
--- a/docs/html/design/media/progress_download.png
+++ b/docs/html/design/media/progress_download.png
Binary files differ
diff --git a/docs/html/design/media/settings_checkbox.png b/docs/html/design/media/settings_checkbox.png
new file mode 100644
index 0000000..6615bfb
--- /dev/null
+++ b/docs/html/design/media/settings_checkbox.png
Binary files differ
diff --git a/docs/html/design/media/settings_date_time.png b/docs/html/design/media/settings_date_time.png
new file mode 100644
index 0000000..8df92d4
--- /dev/null
+++ b/docs/html/design/media/settings_date_time.png
Binary files differ
diff --git a/docs/html/design/media/settings_dependency.png b/docs/html/design/media/settings_dependency.png
new file mode 100644
index 0000000..4821c61
--- /dev/null
+++ b/docs/html/design/media/settings_dependency.png
Binary files differ
diff --git a/docs/html/design/media/settings_flowchart.png b/docs/html/design/media/settings_flowchart.png
new file mode 100644
index 0000000..7e8623c
--- /dev/null
+++ b/docs/html/design/media/settings_flowchart.png
Binary files differ
diff --git a/docs/html/design/media/settings_grouping.png b/docs/html/design/media/settings_grouping.png
new file mode 100644
index 0000000..d271ea8
--- /dev/null
+++ b/docs/html/design/media/settings_grouping.png
Binary files differ
diff --git a/docs/html/design/media/settings_individual_on_off.png b/docs/html/design/media/settings_individual_on_off.png
new file mode 100644
index 0000000..03bea0b
--- /dev/null
+++ b/docs/html/design/media/settings_individual_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_list_subscreen.png b/docs/html/design/media/settings_list_subscreen.png
new file mode 100644
index 0000000..385aa6a
--- /dev/null
+++ b/docs/html/design/media/settings_list_subscreen.png
Binary files differ
diff --git a/docs/html/design/media/settings_main.png b/docs/html/design/media/settings_main.png
new file mode 100644
index 0000000..f42a358
--- /dev/null
+++ b/docs/html/design/media/settings_main.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off.png b/docs/html/design/media/settings_master_on_off.png
new file mode 100644
index 0000000..e46bb97
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off_2.png b/docs/html/design/media/settings_master_on_off_2.png
new file mode 100644
index 0000000..ab4e992
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off_2.png
Binary files differ
diff --git a/docs/html/design/media/settings_multiple_choice.png b/docs/html/design/media/settings_multiple_choice.png
new file mode 100644
index 0000000..9b28566
--- /dev/null
+++ b/docs/html/design/media/settings_multiple_choice.png
Binary files differ
diff --git a/docs/html/design/media/settings_overflow.png b/docs/html/design/media/settings_overflow.png
new file mode 100644
index 0000000..9000bec
--- /dev/null
+++ b/docs/html/design/media/settings_overflow.png
Binary files differ
diff --git a/docs/html/design/media/settings_slider.png b/docs/html/design/media/settings_slider.png
new file mode 100644
index 0000000..51545c8
--- /dev/null
+++ b/docs/html/design/media/settings_slider.png
Binary files differ
diff --git a/docs/html/design/media/settings_subscreen_navigation.png b/docs/html/design/media/settings_subscreen_navigation.png
new file mode 100644
index 0000000..2ab0b96
--- /dev/null
+++ b/docs/html/design/media/settings_subscreen_navigation.png
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.mp4 b/docs/html/design/media/tabs_scrolly.mp4
index 4329243..dc9e9c6 100644
--- a/docs/html/design/media/tabs_scrolly.mp4
+++ b/docs/html/design/media/tabs_scrolly.mp4
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.ogv b/docs/html/design/media/tabs_scrolly.ogv
index 345e57a..3e484f9 100644
--- a/docs/html/design/media/tabs_scrolly.ogv
+++ b/docs/html/design/media/tabs_scrolly.ogv
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.webm b/docs/html/design/media/tabs_scrolly.webm
index 17e368e..e9d371d 100644
--- a/docs/html/design/media/tabs_scrolly.webm
+++ b/docs/html/design/media/tabs_scrolly.webm
Binary files differ
diff --git a/docs/html/design/media/ui_overview_all_apps.png b/docs/html/design/media/ui_overview_all_apps.png
index 467f5ad..17e7ece 100644
--- a/docs/html/design/media/ui_overview_all_apps.png
+++ b/docs/html/design/media/ui_overview_all_apps.png
Binary files differ
diff --git a/docs/html/design/patterns/actionbar.jd b/docs/html/design/patterns/actionbar.jd
index 9e3f48c..2226fec 100644
--- a/docs/html/design/patterns/actionbar.jd
+++ b/docs/html/design/patterns/actionbar.jd
@@ -176,7 +176,7 @@
       <source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
     </video>
     <div class="figure-caption">
-      Scrolling tabs in Google Play.
+      Scrolling tabs in the Play Store app.
       <div class="video-instructions">&nbsp;</div>
     </div>
 
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd
index b54b12f..e2398ed 100644
--- a/docs/html/design/patterns/app-structure.jd
+++ b/docs/html/design/patterns/app-structure.jd
@@ -7,7 +7,7 @@
   single screen</li>
 <li>Apps such as Phone whose main purpose is to switch between different activities without deeper
   navigation</li>
-<li>Apps such as Gmail or Google Play that combine a broad set of data views with deep navigation</li>
+<li>Apps such as Gmail or the Play Store that combine a broad set of data views with deep navigation</li>
 </ul>
 <p>Your app's structure depends largely on the content and tasks you want to surface for your users.</p>
 <h2 id="general-structure">General Structure</h2>
@@ -60,7 +60,7 @@
 
     <img src="{@docRoot}design/media/app_structure_market.png">
     <div class="figure-caption">
-      The Google Play app's start screen primarily allows navigation into the stores for Apps, Music, Books,
+      The Play Store app's start screen primarily allows navigation into the stores for Apps, Music, Books,
       Movies and Games. It is also enriched with tailored recommendations and promotions that
       surface content of interest to the user. Search is readily available from the action bar.
     </div>
@@ -145,8 +145,8 @@
 
     <img src="{@docRoot}design/media/app_structure_scrolltabs.png">
     <div class="figure-caption">
-      Google Play uses tabs to simultaneously show category choice and content. To navigate between
-      categories, users can swipe left/right on the content.
+      The Play Store app uses tabs to simultaneously show category choice and content. To navigate
+      between categories, users can swipe left/right on the content.
     </div>
 
   </div>
diff --git a/docs/html/design/patterns/navigation.jd b/docs/html/design/patterns/navigation.jd
index d35cd82..7e288ae 100644
--- a/docs/html/design/patterns/navigation.jd
+++ b/docs/html/design/patterns/navigation.jd
@@ -13,32 +13,36 @@
 
 <h2 id="up-vs-back">Up vs. Back</h2>
 
-<p>The Up button is used to navigate within an application based on the hierarchical relationships
+<p>The Up button is used to navigate within an app based on the hierarchical relationships
 between screens. For instance, if screen A displays a list of items, and selecting an item leads to
 screen B (which presents that item in more detail), then screen B should offer an Up button that
 returns to screen A.</p>
-<p>If a screen is the topmost one in an app (i.e. the home of the app), it should not present an Up
+<p>If a screen is the topmost one in an app (that is, the app's home), it should not present an Up
 button.</p>
-<p>The system Back key is used to navigate based on the history of screens the user has recently seen,
-in reverse chronological order&mdash;in effect, the temporal relationships between screens.</p>
+
+<p>The system Back button is used to navigate, in reverse chronological order, through the history
+of screens the user has recently worked with. It is generally based on the temporal relationships
+between screens, rather than the app's hierarchy.</p>
+
 <p>When the previously viewed screen is also the hierarchical parent of the current screen, pressing
-the Back key will have the same result as pressing an Up button -- this is a common occurrence.
-However, unlike the Up button, which ensures the user remains within your app, the Back key can
-return the user to the Home screen, or even to a different application.</p>
+the Back button has the same result as pressing an Up button&mdash;this is a common
+occurrence. However, unlike the Up button, which ensures the user remains within your app, the Back
+button can return the user to the Home screen, or even to a different app.</p>
 
 <img src="{@docRoot}design/media/navigation_up_vs_back_gmail.png">
 
-<p>The Back key also supports a few behaviors not directly tied to screen-to-screen navigation:</p>
+<p>The Back button also supports a few behaviors not directly tied to screen-to-screen navigation:
+</p>
 <ul>
-<li>Back dismisses floating windows (dialogs, popups)</li>
-<li>Back dismisses contextual action bars, and removes the highlight from the selected items</li>
-<li>Back hides the onscreen keyboard (IME)</li>
+<li>Dismisses floating windows (dialogs, popups)</li>
+<li>Dismisses contextual action bars, and removes the highlight from the selected items</li>
+<li>Hides the onscreen keyboard (IME)</li>
 </ul>
 <h2 id="within-app">Navigation Within Your App</h2>
 
 <h4>Navigating to screens with multiple entry points</h4>
 <p>Sometimes a screen doesn't have a strict position within the app's hierarchy, and can be reached
-from multiple entry points&mdash;e.g., a settings screen which can be navigated to from any screen
+from multiple entry points&mdash;such as a settings screen that can be reached from any other screen
 in your app. In this case, the Up button should choose to return to the referring screen, behaving
 identically to Back.</p>
 <h4>Changing view within a screen</h4>
@@ -50,7 +54,7 @@
 <li>Switching views using a dropdown (aka collapsed tabs)</li>
 <li>Filtering a list</li>
 <li>Sorting a list</li>
-<li>Changing display characteristics (e.g. zooming)</li>
+<li>Changing display characteristics (such as zooming)</li>
 </ul>
 <h4>Navigating between sibling screens</h4>
 <p>When your app supports navigation from a list of items to a detail view of one of those items, it's
@@ -61,56 +65,140 @@
 
 <img src="{@docRoot}design/media/navigation_between_siblings_gmail.png">
 
-<p>However, a notable exception to this occurs when browsing between "related" detail views not tied
-together by the referring list&mdash;for example, when browsing on Google Play between apps from
+<p>However, a notable exception to this occurs when browsing between related detail views not tied
+together by the referring list&mdash;for example, when browsing in the Play Store between apps from
 the same developer, or albums by the same artist. In these cases, following each link does create
-history, causing the Back button to step through each screen of related content which has been
-viewed. Up should continue to bypass these related screens and navigate to the most recently viewed
-container screen.</p>
+history, causing the Back button to step through each previously viewed screen. Up should continue
+to bypass these related screens and navigate to the most recently viewed container screen.</p>
 
 <img src="{@docRoot}design/media/navigation_between_siblings_market1.png">
 
 <p>You have the ability to make the Up behavior even smarter based on your knowledge of detail
-view. If we extend our Google Play sample from above, imagine the user has navigated from the last Book
-viewed to the details for the Movie adaptation. In that case, Up can return to a container (Movies)
-which the user had not previously navigated through.</p>
+view. Extending the Play Store example from above, imagine the user has navigated from the last
+Book viewed to the details for the Movie adaptation. In that case, Up can return to a container
+(Movies) which the user hasn't previously navigated through.</p>
 
 <img src="{@docRoot}design/media/navigation_between_siblings_market2.png">
 
-<h2 id="from-outside">Navigation From Outside Your App</h2>
+<h2 id="into-your-app">Navigation into Your App via Home Screen Widgets and Notifications</h2>
 
-<p>There are two categories of navigation from outside your app to screens deep within the app's
-hierarchy:</p>
+<p>You can use Home screen widgets or notifications to help your users navigate directly to screens
+deep within your app's hierarchy. For example, Gmail's Inbox widget and new message notification can
+both bypass the Inbox screen, taking the user directly to a conversation view.</p>
+
+<p>For both of these cases, handle the Up button as follows:</p>
+
 <ul>
-<li>App-to-app navigation, such as via intent completion.</li>
-<li>System-to-app navigation, such as via notifications and home screen widgets.</li>
+<li><em>If the destination screen is typically reached from one particular screen within your
+app</em>, Up should navigate to that screen.</li>
+<li><em>Otherwise</em>, Up should navigate to the topmost ("Home") screen of your app.</li>
 </ul>
-<p>Gmail provides examples of each of these. For app-to-app navigation, a "Share" intent goes directly
-to the compose screen. For system-to-app navigation, both a new message notification and a home
-screen widget can bypass the Inbox screen, taking the user directly to a conversation view.</p>
-<h4>App-to-app navigation</h4>
-<p>When navigating deep into your app's hierarchy directly from another app via an intent, Back will
-return to the referring app.</p>
-<p>The Up button is handled as follows:
-- If the destination screen is typically reached from one particular screen within your app, Up
-  should navigate to that screen.
-- Otherwise, Up should navigate to the topmost ("Home") screen of your app.</p>
-<p>For example, after choosing to share a book being viewed on Google Play, the user navigates directly to
-Gmail's compose screen. From there, Up returns to the Inbox (which happens to be both the
-typical referrer to compose, as well as the topmost screen of the app), while Back returns to
-Google Play.</p>
 
-<img src="{@docRoot}design/media/navigation_from_outside_up.png">
+<p>In the case of the Back button, you should make navigation more predictable by inserting into the
+task's back stack the complete upward navigation path to the app's topmost screen. This allows users
+who've forgotten how they entered your app to navigate to the app's topmost screen before
+exiting.</p>
 
-<h4>System-to-app navigation</h4>
-<p>If your app was reached via the system mechanisms of notifications or home screen widgets, Up
-behaves as described for app-to-app navigation, above.</p>
-<p>For the Back key, you should make navigation more predictably by inserting into the task's back
-stack the complete upward navigation path to the app's topmost screen. This way, a user who has
-forgotten how they entered your app can safely navigate to the app's topmost screen before exiting
-it.</p>
-<p>For example, Gmail's Home screen widget has a button for diving directly to its compose screen.
-After following that path, the Back key first returns to the Inbox, and from there continues to
-Home.</p>
+<p>As an example, Gmail's Home screen widget has a button for diving directly to its compose
+screen. Up or Back from the compose screen would take the user to the Inbox, and from there the
+Back button continues to Home.</p>
 
 <img src="{@docRoot}design/media/navigation_from_outside_back.png">
+
+<h4>Indirect notifications</h4>
+
+<p>When your app needs to present information about multiple events simultaneously, it can use a
+single notification that directs the user to an interstitial screen. This screen summarizes these
+events, and provides paths for the user to dive deeply into the app. Notifications of this style are
+called <em>indirect notifications</em>.</p>
+
+<p>Unlike standard (direct) notifications, pressing Back from an indirect notification's
+interstitial screen returns the user to the point the notification was triggered from&mdash;no
+additional screens are inserted into the back stack. Once the user proceeds into the app from its
+interstitial screen, Up and Back behave as for standard notifications, as described above:
+navigating within the app rather than returning to the interstitial.</p>
+
+<p>For example, suppose a user in Gmail receives an indirect notification from Calendar. Touching
+this notification opens the interstitial screen, which displays reminders for several different
+events. Touching Back from the interstitial returns the user to Gmail. Touching on a particular
+event takes the user away from the interstitial and into the full Calendar app to display details of
+the event. From the event details, Up and Back navigate to the top-level view of Calendar.</p>
+
+<img src="{@docRoot}design/media/navigation_indirect_notification.png">
+
+<h4>Pop-up notifications</h4>
+
+<p><em>Pop-up notifications</em> bypass the notification drawer, instead appearing directly in
+front of the user. They are rarely used, and <strong>should be reserved for occasions where a timely
+response is required and the interruption of the user's context is necessary</strong>. For example,
+Talk uses this style to alert the user of an invitation from a friend to join a video chat, as this
+invitation will automatically expire after a few seconds.</p>
+
+<p>In terms of navigation behavior, pop-up notifications closely follow the behavior of an indirect
+notification's interstitial screen. Back dismisses the pop-up notification. If the user navigates
+from the pop-up into the notifying app, Up and Back follow the rules for standard notifications,
+navigating within the app.</p>
+
+<img src="{@docRoot}design/media/navigation_popup_notification.png">
+
+<h2 id="between-apps">Navigation Between Apps</h2>
+
+<p>One of the fundamental strengths of the Android system is the ability for apps to activate each
+other, giving the user the ability to navigate directly from one app into another. For example, an
+app that needs to capture a photo can activate the Camera app, which will return the photo
+to the referring app. This is a tremendous benefit to both the developer, who can easily leverage
+code from other apps, and the user, who enjoys a consistent experience for commonly performed
+actions.</p>
+
+<p>To understand app-to-app navigation, it's important to understand the Android framework behavior
+discussed below.</p>
+
+<h4>Activities, tasks, and intents</h4>
+
+<p>In Android, an <strong>activity</strong> is an application component that defines a screen of
+information and all of the associated actions the user can perform. Your app is a collection of
+activities, consisting of both the activities you create and those you re-use from other apps.</p>
+
+<p>A <strong>task</strong> is the sequence of activities a user follows to accomplish a goal. A
+single task can make use of activities from just one app, or may draw on activities from a number
+of different apps.</p>
+
+<p>An <strong>intent</strong> is a mechanism for one app to signal it would like another
+app's assistance in performing an action. An app's activities can indicate which intents
+they can respond to. For common intents such as "Share", the user may have many apps installed
+that can fulfill that request.</p>
+
+<h4>Example: navigating between apps to support sharing</h4>
+
+<p>To understand how activities, tasks, and intents work together, consider how one app allows users
+to share content by using another app. For example, launching the Play Store app from Home begins
+new Task A (see figure below). After navigating through the Play Store and touching a promoted book
+to see its details, the user remains in the same task, extending it by adding activities. Triggering
+the Share action prompts the user with a dialog listing each of the activities (from different apps)
+which have registered to handle the Share intent.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_inward.png">
+
+<p>When the user elects to share via Gmail, Gmail's compose activity is added as a continuation of
+Task A&mdash;no new task is created. If Gmail had its own task running in the background, it would
+be unaffected.</p>
+
+<p>From the compose activity, sending the message or touching the Back button returns the user to
+the book details activity. Subsequent touches of Back continue to navigate back through the Play
+Store, ultimately arriving at Home.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_back.png">
+
+<p>However, by touching Up from the compose activity, the user indicates a desire to remain within
+Gmail. Gmail's conversation list activity appears, and a new Task B is created for it. New tasks are
+always rooted to Home, so touching Back from the conversation list returns there.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_up.png">
+
+<p>Task A persists in the background, and the user may return to it later (for example, via the
+Recents screen). If Gmail already had its own task running in the background, it would be replaced
+with Task B&mdash;the prior context is abandoned in favor of the user's new goal.</p>
+
+<p>When your app registers to handle intents with an activity deep within the app's hierarchy,
+refer to <a href="#into-your-app">Navigation into Your App via Home Screen Widgets and
+Notifications</a> for guidance on how to specify Up navigation.</p>
diff --git a/docs/html/design/patterns/pure-android.jd b/docs/html/design/patterns/pure-android.jd
index 8ed1347..77813c0 100644
--- a/docs/html/design/patterns/pure-android.jd
+++ b/docs/html/design/patterns/pure-android.jd
@@ -48,7 +48,8 @@
 document or deleting.</p>
 <p>As you are migrating your app to Android, please swap out platform-specific icons with their Android
 counterparts.</p>
-<p>You can find a wide variety of icons for use in your app in the Android SDK.</p>
+<p>You can find a wide variety of icons for use in your app on the
+<a href="{@docRoot}design/downloads/index.html">Downloads</a> page.</p>
 
   </div>
   <div class="layout-content-col span-8">
@@ -89,6 +90,33 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-5">
 
+<h4>Don't hardcode links to other apps</h4>
+<p>In some cases you might want your app to take advantage of another app's feature set. For
+example, you may want to share the content that your app created via a social network or messaging
+app, or view the content of a weblink in a browser. Don't use hard-coded, explicit links to
+particular apps to achieve this. Instead, use Android's intent API to launch an activity chooser
+which lists all applications that are set up to handle the particular request. This lets the user
+complete the task with their preferred app. For sharing in particular, consider using the <em>Share
+Action Provider</em> in your action bar to provide faster access to the user's most recently used
+sharing target.</p>
+
+  </div>
+  <div class="layout-content-col span-8">
+
+    <img src="{@docRoot}design/media/migrating_intents.png">
+    <div class="figure-caption">
+      Link to other apps with the activity chooser or use the <em>Share Action Provider</em> in the
+      action bar.
+    </div>
+
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
 <h4>Don't use labeled back buttons on action bars</h4>
 <p>Other platforms use an explicit back button with label to allow the user to navigate up the
 application's hierarchy. Instead, Android uses the main action bar's app icon for hierarchical
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
new file mode 100644
index 0000000..3b28b84
--- /dev/null
+++ b/docs/html/design/patterns/settings.jd
@@ -0,0 +1,689 @@
+page.title=Settings
+@jd:body
+
+<p>Settings is a place in your app where users indicate their preferences for how your app should
+behave. This benefits users because:</p>
+
+<ul>
+<li>You don't need to interrupt them with the same questions over and over when certain situations
+arise. The settings predetermine what will always happen in those situations (see design
+principle: <a href="{@docRoot}design/get-started/principles.html#decide-for-me">Decide for me but
+let me have the final say</a>).</li>
+<li>You help them feel at home and in control (see design principle:
+<a href="{@docRoot}design/get-started/principles.html#make-it-mine">Let me make it mine</a>).</li>
+</ul>
+
+<h2 id="flow-structure">Flow and Structure</h2>
+
+<h4 id="settings-access">Provide access to Settings in the action overflow</h4>
+
+<p>Settings is given low prominence in the UI because it's not frequently needed. Even if there's
+room in the <a href="{@docRoot}design/patterns/actionbar.html">action bar</a>, never make Settings
+an action button. Always keep it in the action overflow and label it "Settings". Place it below
+all other items except "Help".</p>
+
+<img src="{@docRoot}design/media/settings_overflow.png">
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4 id="what-to-make-a-setting">Avoid the temptation to make everything a setting</h4>
+
+<p>Because Settings is a few navigational steps away, no matter how many items you have, they'll
+never clutter up the core part of your UI. This may seem like good news, but it also poses a
+challenge.</p>
+
+<p>Settings can be a tempting place to keep a lot of stuff&mdash;like a hall closet where things
+get stashed when you tidy up before company comes over. It's not a place where you spend lots of
+time, so it's easy to rationalize and ignore its cluttered condition. But when users visit
+Settings&mdash;however infrequently&mdash;they'll have the same expectations for the experience as
+they do everywhere else in your app. More settings means more choices to make, and too many are
+overwhelming.</p>
+
+<p>So don't punt on the difficult product decisions and debates that can bring on the urge to
+"just make it a setting". For each control you're considering adding to Settings, make sure it
+meets the bar:</p>
+
+<img src="{@docRoot}design/media/settings_flowchart.png">
+
+<div class="vspace size-3">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5 with-callouts">
+
+<h4 id="group-settings">If you still have lots of settings, group related settings together</h4>
+
+<p>The number of items an average human can hold in short-term memory is 7&plusmn;2. If you
+present a list of 10 or more settings (even after applying the criteria above), users will have
+more difficulty scanning, comprehending, and processing them.</p>
+
+<p>You can remedy this by dividing some or all of the settings into groups, effectively turning
+one long list into multiple shorter lists. A group of related settings can be presented in one of
+two ways:</p>
+
+<ol>
+<li><h4>Under a section divider</h4></li>
+<li><h4>In a separate subscreen</h4></li>
+</ol>
+
+<p>You can use one or both these grouping techniques to organize your app's settings.</p>
+
+<p>For example, in the main screen of the Android Settings app, each item in the list navigates
+to a subscreen of related settings. In addition, the items themselves are grouped under section
+dividers.</p>
+
+  </div>
+  <div class="layout-content-col span-8">
+
+    <img src="{@docRoot}design/media/settings_grouping.png">
+
+  </div>
+</div>
+
+<p>Grouping settings is not an exact science, but here's some advice for how to approach it, based
+on the total number of settings in your app.</p>
+
+<div class="vspace size-1">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-2">
+
+<h4>7 or fewer</h4>
+
+  </div>
+  <div class="layout-content-col span-11">
+
+<p>Don't group them at all. It won't benefit users and will seem like overkill.</p>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-2">
+
+<h4>8 to 10</h4>
+
+  </div>
+  <div class="layout-content-col span-11">
+
+<p>Try grouping related settings under 1 or 2 section dividers. If you have any "singletons"
+(settings that don't relate to any other settings and can't be grouped under your section
+dividers), treat them as follows:</p>
+
+<ul>
+<li>If they include some of your most important settings, list them at the top without a section
+divider.</li>
+<li>Otherwise, list them at the bottom with a section divider called "OTHER", in order of
+importance.</li>
+</ul>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-2">
+
+<h4>11 to 15</h4>
+
+  </div>
+  <div class="layout-content-col span-11">
+
+<p>Same advice as above, but try 2 to 4 section dividers.</p>
+
+<p>Also, try the following to reduce the list:</p>
+
+<ul>
+<li>If 2 or more of the settings are mainly for power users, move them out of your main Settings
+screen and into an "Advanced" subscreen. Place an item in the action overflow called "Advanced" to
+navigate to it.</li>
+<li>Look for "doubles": two settings that relate to one another, but not to any other settings.
+Try to combine them into one setting, using the design patterns described later in this section.
+For example, you might be able to redesign two related checkbox settings into one multiple choice
+setting.</li>
+</ul>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-2">
+
+<h4>16 or more</h4>
+
+  </div>
+  <div class="layout-content-col span-11">
+
+<p>If you have any instances of 4 or more related settings, group them under a subscreen. Then use
+the advice suggested above for the reduced list size.</p>
+
+  </div>
+</div>
+
+
+<h2 id="patterns">Design Patterns</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Checkbox</h4>
+<p>Use this pattern for a setting that is either selected or not selected.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_checkbox.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Multiple choice</h4>
+<p>Use this pattern for a setting that needs to present a discrete set of options, from which the
+user can choose only one.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_multiple_choice.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Slider</h4>
+<p>Use this pattern for a setting where the range of values are not discrete and fall along a
+continuum.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_slider.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Date/time</h4>
+<p>Use this pattern for a setting that needs to collect a date and/or time from the user.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_date_time.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Subscreen navigation</h4>
+<p>Use this pattern for navigating to a subscreen or sequence of subscreens that guide the user
+through a more complex setup process.</p>
+<ul>
+<li>If navigating to a single subscreen, use the same title in both the subscreen and the label
+navigating to it.</li>
+<li>If navigating to a sequence of subscreens (as in this example), use a title that describes the
+first step in the sequence.</li>
+</ul>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_subscreen_navigation.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>List subscreen</h4>
+<p>Use this pattern for a setting or category of settings that contains a list of equivalent items.
+</p>
+<p>The label provides the name of the item, and secondary text may be used for status. (In this
+example, status is reinforced with an icon to the right of the label.) Any actions associated with
+the list appear in the action bar rather than the list itself.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_list_subscreen.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Master on/off switch</h4>
+<p>Use this pattern for a category of settings that need a mechanism for turning on or off as a
+whole.</p>
+<p>An on/off switch is placed as the first item in the action bar of a subscreen. When the switch
+is turned off, the items in the list disappear, replaced by text that describes why the list is
+empty. If any actions require the switch to be on, they become disabled.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<div class="vspace size-2">&nbsp;</div>
+
+<p>You can also echo the master on/off switch in the menu item that leads to the subscreen.
+However, you should only do this in cases where users rarely need to access the subscreen once
+it's initially set up and more often just want to toggle the switch.</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off_2.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Individual on/off switch</h4>
+<p>Use this pattern for an individual setting that requires a more elaborate description than can
+be provided in checkbox form.</p>
+<p>The on/off switch only appears in the subscreen so that users aren't able to toggle it without
+also being exposed to the descriptive text. Secondary text appears below the setting label to
+reflect the current selection.</p>
+<p>In this example, Android Beam is on by default. Since users might not know what this setting
+does, we made the status more descriptive than just "On".</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_individual_on_off.png">
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-3">
+
+<h4>Dependency</h4>
+<p>Use this pattern for a setting that changes availability based on the value of another setting.
+</p>
+<p>The disabled setting appears below its dependency, without any indentation. If the setting
+includes a status line, it says "Unavailable", and if the reason isn't obvious, a brief
+explanation is included in the status.</p>
+<p>If a given setting is a dependency to 3 or more settings, consider using a subscreen with a
+master on/off switch so that your main settings screen isn't cluttered by lots of disabled items.
+</p>
+
+  </div>
+  <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_dependency.png">
+
+  </div>
+</div>
+
+<h2 id="defaults">Defaults</h2>
+
+<p>Take great care in choosing default values for each of your settings. Because settings
+determine app behavior, your choices will contribute to users' first impressions of your app. Even
+though users can change settings, they'll expect the initial states to be sensible. The following
+questions (when applicable) may help inform your decisions:</p>
+
+<ul>
+<li>Which choice would most users be likely to choose on their own if there were no default?</li>
+<li>Which choice is the most neutral or middle-of-the-road?</li>
+<li>Which choice is the least risky, controversial, or over-the-top?</li>
+<li>Which choice uses the least amount of battery or mobile data?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#never-lose-my-stuff">Never lose my stuff</a>?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#interrupt-only-if-important">Only interrupt
+me if it's important</a>?
+</li>
+</ul>
+
+<h2 id="writing">Writing Guidelines</h2>
+
+<h4>Label clearly and concisely</h4>
+
+<p>Writing a good label for a setting can be challenging because space is very limited. You only
+get one line, and it's incredibly short on the smallest of devices. Follow these guidelines to
+make your labels brief, meaningful, and scannable:</p>
+
+<ul>
+<li>Write each label in sentence case (i.e. only the first word and proper nouns are capitalized).
+</li>
+<li>Don't start a label with an instructional verb like "Set", "Change", "Edit", "Modify",
+"Manage", "Use", "Select", or "Choose". Users already understand that they can do these things to
+settings.</li>
+<li>Likewise, don't end a label with a word like "setting" or "settings". It's already implied.
+</li>
+<li>If the setting is part of a grouping, don't repeat the word(s) used in the section divider or
+subscreen title.</li>
+<li>Avoid starting a label with a negative word like "Don't" or "Never". For example, "Don't
+allow" could be rephrased to "Block".</li>
+<li>Steer clear of technical jargon as much as possible, unless it's a term widely understood by
+your target users. Use common verbs and nouns to convey the setting's purpose rather than its
+underlying technology.</li>
+<li>Don't refer to the user. For example, for a setting allowing the user to turn notifications on
+or off, label it "Notifications" instead of "Notify me".</li>
+</ul>
+
+<p>Once you've decided on labels for your settings, be sure to preview them on an
+<a href="{@docRoot}design/style/metrics-grids.html">LDPI handset</a> in portrait to make sure
+they'll fit everywhere.</p>
+
+<h4>Secondary text below is for status, not description&hellip;</h4>
+
+<p>Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe
+it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status.
+</p>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+    <table class="ui-table bad emulate-content-left-padding">
+    <thead>
+      <tr>
+        <th class="label">
+        Screen timeout
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        Adjust the delay before the screen automatically turns off
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label good">After</div>
+
+    <table class="ui-table good">
+    <thead>
+      <tr>
+        <th class="label">
+        Sleep
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        After 10 minutes of activity
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+</div>
+
+<p>Status in secondary text has the following benefits:</p>
+<ul>
+<li>Users can see at a glance what the current value of a setting is without having to navigate
+any further.</li>
+<li>It applies the design principle
+<a href="{@docRoot}design/get-started/principles.html#keep-it-brief">Keep it brief</a>, which
+users greatly appreciate.</li>
+</ul>
+
+<h4>&hellip;unless it's a checkbox setting</h4>
+<p>There's one important exception to the using secondary text for status: checkbox settings.
+Here, use secondary text for description, not status. Status below a checkbox is unnecessary
+because the checkbox already indicates it. The reason why it's appropriate to have a description
+below a checkbox setting is because&mdash;unlike other controls&mdash;it doesn't display a dialog
+or navigate to another screen where additional information can be provided.</p>
+
+<p>That said, if a checkbox setting's label is clear enough on its own, there's no need to also
+provide a description. Only include one if necessary.</p>
+
+<p>Follow these guidelines to write checkbox setting descriptions:</p>
+<ul>
+<li>Keep it to one sentence and don't use ending punctuation.</li>
+<li>Convey what happens when the setting is checked, phrased in the form of a command. Example:
+"Allow data exchange", not "Allows data exchange".</li>
+<li>Avoid repetition by choosing words that don't already appear in the label.</li>
+<li>Don't refer to the user unless it's necessary for understanding the setting.</li>
+<li>If you must refer to the user, do so in the second person ("you") rather than the first person
+("I"). Android speaks to users, not on behalf of them.</li>
+</ul>
+
+<h4>Writing examples</h4>
+
+<p>The following are examples of changes we made to labels and secondary text in the Settings app
+in Ice Cream Sandwich.</p>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+    <table class="ui-table bad emulate-content-left-padding">
+    <thead>
+      <tr>
+        <th class="label">
+        Use tactile feedback
+        </th>
+      </tr>
+    </thead>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label good">After</div>
+
+    <table class="ui-table good">
+    <thead>
+      <tr>
+        <th class="label">
+        Vibrate on touch
+        </th>
+      </tr>
+    </thead>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-5">
+
+<p>In this checkbox setting, we eliminated the throwaway word "Use" and rephrased the label to be
+more direct and understandable.</p>
+
+  </div>
+
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+    <table class="ui-table bad emulate-content-left-padding">
+    <thead>
+      <tr>
+        <th class="label">
+        Screen timeout
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        Adjust the delay before the screen automatically turns off
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label good">After</div>
+
+    <table class="ui-table good">
+    <thead>
+      <tr>
+        <th class="label">
+        Sleep
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        After 10 minutes of activity
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-5">
+
+<p>In this multiple choice setting, we changed the label to a friendlier term and also replaced
+the description with status. We put some descriptive words around the selected value, "10
+minutes", because on its own, the meaning could be misinterpreted as "sleep for 10 minutes".</p>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+    <table class="ui-table bad emulate-content-left-padding">
+    <thead>
+      <tr>
+        <th class="label">
+        Change screen lock
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        Change or disable pattern, PIN, or password security
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label good">After</div>
+
+    <table class="ui-table good">
+    <thead>
+      <tr>
+        <th class="label">
+        Screen lock
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        Pattern
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-5">
+
+<p>This setting navigates to a a sequence of subscreens that allow users to choose a type of
+screen lock and then set it up. We eliminated the throwaway word "Change" in the label, and
+replaced the description with the current type of screen lock set up by the user. If the user
+hasn't set up a screen lock, the secondary text says "None".</p>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+    <table class="ui-table bad emulate-content-left-padding">
+    <thead>
+      <tr>
+        <th class="label">
+        NFC
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        Use Near Field Communication to read and exchange tags
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <div class="do-dont-label good">After</div>
+
+    <table class="ui-table good">
+    <thead>
+      <tr>
+        <th class="label">
+        NFC
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="secondary-text">
+        Allow data exchange when the phone touches another device
+        </td>
+      </tr>
+    </tbody>
+    </table>
+
+  </div>
+  <div class="layout-content-col span-5">
+
+<p>In this checkbox setting&mdash;although it's technical jargon&mdash;we kept the "NFC" label
+because: (1) we couldn't find a clear, concise alternative, and (2) user familiarity with the
+acronym is expected to increase dramatically in the next couple of years.</p>
+<p>We did, however, rewrite the description. It's far less technical than before and does a better
+job of conveying how and why you'd use NFC. We didn't include what the acronym stands for because
+it doesn't mean anything to most users and would have taken up a lot of space.</p>
+
+  </div>
+</div>
+
+<h2 id="checklist">Checklist</h2>
+<ul>
+<li><p>Make sure each item in Settings meets the criteria for belonging there.</p></li>
+<li><p>If you have more than 7 items, explore ways to group related settings.</p></li>
+<li><p>Use design patterns wherever applicable so users don't face a learning curve.</p></li>
+<li><p>Choose defaults that are safe, neutral, and fit the majority of users.</p></li>
+<li><p>Give each setting a clear, concise label and use secondary text appropriately.</p></li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/design/style/writing.jd b/docs/html/design/style/writing.jd
index 80fd03e..919ea7aa 100644
--- a/docs/html/design/style/writing.jd
+++ b/docs/html/design/style/writing.jd
@@ -1,58 +1,6 @@
 page.title=Writing Style
 @jd:body
 
-<style>
-
-/* UI tables */
-
-.ui_table {
-  width: 100%;
-  background: #282828;
-  color: #fff;
-  border-radius: 2px;
-  box-shadow: 0 2px 4px rgba(0,0,0,0.25);
-  border-collapse: separate;
-}
-
-.ui_table th,
-.ui_table td {
-  padding: 5px 10px;
-}
-
-.ui_table thead th {
-  font-weight: 600;
-}
-
-.ui_table tfoot td {
-  border-top: 1px solid #494949;
-  border-right: 1px solid #494949;
-  text-align: center;
-}
-
-.ui_table tfoot td:last-child {
-  border-right: 0;
-}
-
-.list_item_margins {
-  margin-left: 30px !important;
-}
-
-.example_label {
-  margin-bottom: 10px;
-  padding-left: 20px;
-  background: transparent none no-repeat scroll 0px 3px;
-}
-
-.example_label.bad {
-  background-image: url({@docRoot}assets/design/ico_wrong.png);
-}
-
-.example_label.good {
-  background-image: url({@docRoot}assets/design/ico_good.png);
-}
-
-</style>
-
 <p>When choosing words for your app:</p>
 <ol>
 <li>
@@ -90,20 +38,20 @@
 <ol><li class="value-1"><strong>Keep it brief.</strong> From the setup wizard:</ol>
 
 <div class="layout-content-row">
-  <div class="layout-content-col span-6 list_item_margins">
+  <div class="layout-content-col span-6 layout-with-list-item-margins">
 
-    <div class="example_label bad">Too formal</div>
+    <div class="do-dont-label bad">Too formal</div>
 
-    <table class="ui_table good"><tbody><tr><td>
+    <table class="ui-table good"><tbody><tr><td>
     Consult the documentation that came with your phone for further instructions.
     </td></tr></tbody></table>
 
   </div>
   <div class="layout-content-col span-6">
 
-    <div class="example_label good">Better</div>
+    <div class="do-dont-label good">Better</div>
 
-    <table class="ui_table good"><tbody><tr><td>
+    <table class="ui-table good"><tbody><tr><td>
     Read the instructions that came with your phone.
     </td></tr></tbody></table>
 
@@ -115,11 +63,11 @@
 <ol><li class="value-2"><strong>Keep it simple.</strong> From the Location settings screen:</ol>
 
 <div class="layout-content-row">
-  <div class="layout-content-col span-6 list_item_margins">
+  <div class="layout-content-col span-6 layout-with-list-item-margins">
 
-    <div class="example_label bad">Confusing</div>
+    <div class="do-dont-label bad">Confusing</div>
 
-    <table class="ui_table bad">
+    <table class="ui-table bad">
     <thead>
       <tr>
         <th>
@@ -139,9 +87,9 @@
   </div>
   <div class="layout-content-col span-6">
 
-    <div class="example_label good">Better</div>
+    <div class="do-dont-label good">Better</div>
 
-    <table class="ui_table good">
+    <table class="ui-table good">
     <thead>
       <tr>
         <th>
@@ -167,12 +115,12 @@
 crashes:</ol>
 
 <div class="layout-content-row">
-  <div class="layout-content-col span-6 list_item_margins">
+  <div class="layout-content-col span-6 layout-with-list-item-margins">
 
-    <div class="example_label bad">Confusing and annoying&mdash;"Sorry" just rubs salt in the
+    <div class="do-dont-label bad">Confusing and annoying&mdash;"Sorry" just rubs salt in the
     wound.</div>
 
-    <table class="ui_table bad">
+    <table class="ui-table bad">
     <thead>
       <tr>
         <th colspan="3">
@@ -200,9 +148,9 @@
   </div>
   <div class="layout-content-col span-6">
 
-    <div class="example_label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
+    <div class="do-dont-label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
 
-    <table class="ui_table good">
+    <table class="ui-table good">
     <thead>
       <tr>
         <th colspan="3">
@@ -234,20 +182,20 @@
 <ol><li class="value-4"><strong>Put the most important thing first.</strong></ol>
 
 <div class="layout-content-row">
-  <div class="layout-content-col span-6 list_item_margins">
+  <div class="layout-content-col span-6 layout-with-list-item-margins">
 
-    <div class="example_label bad">Top news last</div>
+    <div class="do-dont-label bad">Top news last</div>
 
-    <table class="ui_table bad"><tbody><tr><td>
+    <table class="ui-table bad"><tbody><tr><td>
     77 other people +1'd this, including Larry Page.
     </td></tr></tbody></table>
 
   </div>
   <div class="layout-content-col span-6">
 
-    <div class="example_label good">Top news first</div>
+    <div class="do-dont-label good">Top news first</div>
 
-    <table class="ui_table good"><tbody><tr><td>
+    <table class="ui-table good"><tbody><tr><td>
     Larry Page and 77 others +1'd this.
     </td></tr></tbody></table>
 
@@ -255,20 +203,20 @@
 </div>
 
 <div class="layout-content-row">
-  <div class="layout-content-col span-6 list_item_margins">
+  <div class="layout-content-col span-6 layout-with-list-item-margins">
 
-    <div class="example_label bad">Task last</div>
+    <div class="do-dont-label bad">Task last</div>
 
-    <table class="ui_table bad"><tbody><tr><td>
+    <table class="ui-table bad"><tbody><tr><td>
     Touch Next to complete setup using a Wi-Fi connection.
     </td></tr></tbody></table>
 
   </div>
   <div class="layout-content-col span-6">
 
-    <div class="example_label good">Task first</div>
+    <div class="do-dont-label good">Task first</div>
 
-    <table class="ui_table good"><tbody><tr><td>
+    <table class="ui-table good"><tbody><tr><td>
     To finish setup using Wi-Fi, touch Next.
     </td></tr></tbody></table>
 
@@ -280,11 +228,11 @@
 <ol><li class="value-5"><strong>Describe only what's necessary, and no more.</strong></ol>
 
 <div class="layout-content-row">
-  <div class="layout-content-col span-6 list_item_margins">
+  <div class="layout-content-col span-6 layout-with-list-item-margins">
 
-    <div class="example_label bad">From a Setup Wizard screen</div>
+    <div class="do-dont-label bad">From a Setup Wizard screen</div>
 
-    <table class="ui_table bad">
+    <table class="ui-table bad">
     <thead>
       <tr>
         <th>
@@ -306,9 +254,9 @@
   </div>
   <div class="layout-content-col span-6">
 
-    <div class="example_label good">From a Setup Wizard screen</div>
+    <div class="do-dont-label good">From a Setup Wizard screen</div>
 
-    <table class="ui_table good">
+    <table class="ui-table good">
     <thead>
       <tr>
         <th>
diff --git a/docs/html/images/training/cool-places.png b/docs/html/images/training/cool-places.png
new file mode 100755
index 0000000..769b5b7
--- /dev/null
+++ b/docs/html/images/training/cool-places.png
Binary files differ
diff --git a/docs/html/images/training/panoramio-grid.png b/docs/html/images/training/panoramio-grid.png
new file mode 100755
index 0000000..45c0eb5
--- /dev/null
+++ b/docs/html/images/training/panoramio-grid.png
Binary files differ
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index e4ab16f..bbbe6fb1 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -278,6 +278,26 @@
           </a>
           </li>
         </ul>
+       </li>
+       <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>training/tv/index.html">
+           <span class="en">Designing for TV<span class="new">&nbsp;new!</span></span>
+           </a>
+        </div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/tv/optimizing-layouts-tv.html">
+            <span class="en">Optimizing Layouts for TV</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/tv/optimizing-navigation-tv.html">
+            <span class="en">Optimizing Navigation for TV</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/tv/unsupported-features-tv.html">
+            <span class="en">Handling Features Not Supported on TV</span>
+          </a>
+          </li>
+        </ul>
       </li>
 
       <li class="toggle-list">
diff --git a/docs/html/shareables/training/LocationAware.zip b/docs/html/shareables/training/LocationAware.zip
index e1926fa..46970cd 100644
--- a/docs/html/shareables/training/LocationAware.zip
+++ b/docs/html/shareables/training/LocationAware.zip
Binary files differ
diff --git a/docs/html/training/tv/index.jd b/docs/html/training/tv/index.jd
new file mode 100644
index 0000000..ae13c4a
--- /dev/null
+++ b/docs/html/training/tv/index.jd
@@ -0,0 +1,52 @@
+page.title=Designing for TV
+
+trainingnavtop=true
+startpage=true
+next.title=Optimizing layouts for TV
+next.link=optimizing-layouts-tv.html
+
+@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 2.0 (API Level 5) or higher</li>
+</ul>
+
+</div>
+</div>
+<p> 
+  Smart TVs powered by Android bring your favorite Android apps to the best screen in your house. 
+  Thousands of apps in the Google Play Store are already optimized for TVs. This class shows how 
+  you can optimize your Android app for TVs, including how to build a layout that 
+  works great when the user is ten feet away and navigating with a remote control. 
+</p> 
+
+<h2>Lessons</h2> 
+ 
+<dl> 
+  <dt><b><a href="optimizing-layouts-tv.html">Optimizing Layouts for TV</a></b></dt>
+    <dd>Shows you how to optimize app layouts for TV screens, which have some unique characteristics such as:
+    <ul>
+      <li>permanent "landscape" mode</li>
+      <li>high-resolution displays</li>
+      <li>"10 foot UI" environment.</li>
+    </ul>
+    </dd>
+ 
+  <dt><b><a href="optimizing-navigation-tv.html">Optimizing Navigation for TV</a></b></dt>
+    <dd>Shows you how to design navigation for TVs, including: 
+    <ul>
+      <li>handling D-pad navigation</li>
+      <li>providing navigational feedback</li>
+      <li>providing easily-accessible controls on the screen.</li>
+    </ul>
+    </dd>
+
+  <dt><b><a href="unsupported-features-tv.html">Handling features not supported on TV</a></b></dt>
+    <dd>Lists the hardware features that are usually not available on TVs. This lesson also shows you how to 
+    provide alternatives for missing features or check for missing features and disable code at run time.</dd>
+</dl> 
\ No newline at end of file
diff --git a/docs/html/training/tv/optimizing-layouts-tv.jd b/docs/html/training/tv/optimizing-layouts-tv.jd
new file mode 100644
index 0000000..6eac6d3
--- /dev/null
+++ b/docs/html/training/tv/optimizing-layouts-tv.jd
@@ -0,0 +1,246 @@
+page.title=Optimizing Layouts for TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Optimizing Navigation for TV
+next.link=optimizing-navigation-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
+  <li><a href="#MakeTextControlsEasyToSee">Make Text and Controls Easy to See</a></li>
+  <li><a href="#DesignForLargeScreens">Design for High-Density Large Screens</a></li>
+  <li><a href="#HandleLargeBitmaps">Handle Large Bitmaps in Your Application</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+When your application is running on a television set, you should assume that the user is sitting about 
+ten feet away from the screen. This user environment is referred to as the 
+<a href="http://en.wikipedia.org/wiki/10-foot_user_interface">10-foot UI</a>. To provide your 
+users with a usable and enjoyable experience, you should style and lay out your UI accordingly..
+</p>
+<p>
+This lesson shows you how to optimize layouts for TV by:
+</p>
+<ul>
+  <li>Providing appropriate layout resources for landscape mode.</li>
+  <li>Ensuring that text and controls are large enough to be visible from a distance.</li>
+  <li>Providing high resolution bitmaps and icons for HD TV screens.</li>
+</ul>
+
+<h2 id="DesignLandscapeLayouts">Design Landscape Layouts</h2> 
+
+<p>
+TV screens are always in landscape orientation. Follow these tips to build landscape layouts optimized for TV screens:
+</p> 
+<ul>
+  <li>Put on-screen navigational controls on the left or right side of the screen and save the 
+  vertical space for content.</li>
+  <li>Create UIs that are divided into sections, by using <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> 
+  and use view groups like {@link android.widget.GridView} instead 
+  of {@link android.widget.ListView} to make better use of the 
+  horizontal screen space.</li>
+  <li>Use view groups such as {@link android.widget.RelativeLayout} 
+  or {@link android.widget.LinearLayout} to arrange views. 
+  This allows the Android system to adjust the position of the views to the size, alignment, 
+  aspect ratio, and pixel density of the TV screen.</li>
+  <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
+</ul> 
+ 
+<p>
+For example, the following layout is optimized for TV:
+</p>
+
+<img src="{@docRoot}images/training/panoramio-grid.png" />
+
+<p>
+In this layout, the controls are on the lefthand side. The UI is displayed within a 
+{@link android.widget.GridView}, which is well-suited to landscape orientation.
+In this layout both GridView and Fragment have the width and height set 
+dynamically, so they can adjust to the screen resolution. Controls are added to the left side Fragment programatically at runtime.
+The layout file for this UI is {@code res/layout-land-large/photogrid_tv.xml}.
+(This layout file is placed in {@code layout-land-large} because TVs have large screens with landscape orientation. For details refer to 
+<a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.)</p>
+
+res/layout-land-large/photogrid_tv.xml
+<pre>
+&lt;RelativeLayout
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" &gt;
+
+    &lt;fragment
+        android:id="@+id/leftsidecontrols"
+        android:layout_width="0dip"
+        android:layout_marginLeft="5dip"
+        android:layout_height="match_parent" /&gt;
+
+    &lt;GridView        
+        android:id="@+id/gridview"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" /&gt;
+
+&lt;/RelativeLayout>
+</pre>
+
+<p>
+To set up action bar items on the left side of the screen, you can also include the <a
+href="http://code.google.com/p/googletv-android-samples/source/browse/#git%2FLeftNavBarLibrary">
+Left navigation bar library</a> in your application to set up action items on the left side 
+of the screen, instead of creating a custom Fragment to add controls:
+</p>
+
+<pre>
+LeftNavBar bar = (LeftNavBarService.instance()).getLeftNavBar(this);
+</pre>
+
+<p>
+When you have an activity in which the content scrolls vertically, always use a left navigation bar; 
+otherwise, your users have to scroll to the top of the content to switch between the content view and 
+the ActionBar. Look at the  
+<a href="http://code.google.com/p/googletv-android-samples/source/browse/#git%2FLeftNavBarDemo">
+Left navigation bar sample app</a> to see how to simple it is to include the left navigation bar in your app.
+</p>
+
+<h2 id="MakeTextControlsEasyToSee">Make Text and Controls Easy to See</h2>
+<p>
+The text and controls in a TV application's UI should be easily visible and navigable from a distance.
+Follow these tips to make them easier to see from a distance :
+</p>
+
+<ul>
+  <li>Break text into small chunks that users can quickly scan.</li>
+  <li>Use light text on a dark background. This style is easier to read on a TV.</li>
+  <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes. Use simple sans-serif 
+  fonts and use anti-aliasing to increase readability.</li>
+  <li>Use Android's standard font sizes:
+  <pre>
+  &lt;TextView
+        android:id="@+id/atext"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"/&gt;
+  </pre></li>
+  <li>Ensure that all your view widgets are large enough to be clearly visible to someone sitting 10 feet away 
+  from the screen (this distance is greater for very large screens).  The best way to do this is to use 
+  layout-relative sizing rather than absolute sizing, and density-independent pixel units instead of absolute 
+  pixel units. For example, to set the width of a widget, use wrap_content instead of a pixel measurement, 
+  and to set the margin for a widget, use dip instead of px values.
+  </li>
+</ul>
+<p>
+
+</p>
+
+<h2 id="DesignForLargeScreens">Design for High-Density Large Screens</h2>
+
+<p>
+The common HDTV display resolutions are 720p, 1080i, and 1080p. Design your UI for 1080p, and then 
+allow the Android system to downscale your UI to 720p if necessary. In general, downscaling (removing pixels) 
+does not degrade the UI (Notice that the converse is not true; you should avoid upscaling because it degrades 
+UI quality).
+</p>
+
+<p>
+To get the best scaling results for images, provide them as <a href="{@docRoot}guide/developing/tools/draw9patch.html">
+9-patch image</a> elements if possible.
+If you provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or grainy. This 
+is not a good experience for the user. Instead, use high-quality images. 
+</p>
+
+<p>
+For more information on optimizing apps for large screens see <a href="{@docRoot}training/multiscreen/index.html">
+Designing for multiple screens</a>.
+</p>
+
+<h2 id="HandleLargeBitmaps">Design to Handle Large Bitmaps</h2>
+
+<p>
+The Android system has a limited amount of memory, so downloading and storing high-resolution images can often 
+cause out-of-memory errors in your app. To avoid this, follow these tips:
+</p>
+
+<ul>
+  <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in 
+      a {@link android.widget.GridView} or 
+      {@link android.widget.Gallery}, only load an image when 
+      {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()} 
+      is called on the View's {@link android.widget.Adapter}.
+  </li>
+  <li>Call {@link android.graphics.Bitmap#recycle()} on 
+      {@link android.graphics.Bitmap} views that are no longer needed.
+  </li>
+  <li>Use {@link java.lang.ref.WeakReference} for storing references 
+      to {@link android.graphics.Bitmap} objects in a in-memory 
+      <a href="{@link java.util.Collection}.</li>
+  <li>If you fetch images from the network, use {@link android.os.AsyncTask} 
+      to fetch them and store them on the SD card for faster access.
+      Never do network transactions on the application's UI thread.
+  </li>
+  <li>Scale down really large images to a more appropriate size as you download them; otherwise, downloading the image 
+  itself may cause an "Out of Memory" exception. Here is sample code that scales down images while downloading:
+  
+  <pre>
+  // Get the source image's dimensions
+  BitmapFactory.Options options = new BitmapFactory.Options();
+  // This does not download the actual image, just downloads headers.
+  options.inJustDecodeBounds = true; 
+  BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+  // The actual width of the image.
+  int srcWidth = options.outWidth;  
+  // The actual height of the image.
+  int srcHeight = options.outHeight;  
+
+  // Only scale if the source is bigger than the width of the destination view.
+  if(desiredWidth > srcWidth)
+    desiredWidth = srcWidth;
+
+  // Calculate the correct inSampleSize/scale value. This helps reduce memory use. It should be a power of 2.
+  int inSampleSize = 1;
+  while(srcWidth / 2 > desiredWidth){
+    srcWidth /= 2;
+    srcHeight /= 2;
+    inSampleSize *= 2;
+  }
+
+  float desiredScale = (float) desiredWidth / srcWidth;
+
+  // Decode with inSampleSize
+  options.inJustDecodeBounds = false;
+  options.inDither = false;
+  options.inSampleSize = inSampleSize;
+  options.inScaled = false;
+  // Ensures the image stays as a 32-bit ARGB_8888 image.
+  // This preserves image quality.
+  options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
+                                                	
+  Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+
+  // Resize
+  Matrix matrix = new Matrix();
+  matrix.postScale(desiredScale, desiredScale);
+  Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
+      sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
+  sampledSrcBitmap = null;
+
+  // Save
+  FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
+  scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+  scaledBitmap = null;
+   </pre>
+  </li> </ul>
\ No newline at end of file
diff --git a/docs/html/training/tv/optimizing-navigation-tv.jd b/docs/html/training/tv/optimizing-navigation-tv.jd
new file mode 100644
index 0000000..8b5878e
--- /dev/null
+++ b/docs/html/training/tv/optimizing-navigation-tv.jd
@@ -0,0 +1,206 @@
+page.title=Optimizing Navigation for TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Layouts for TV
+previous.link=optimizing-layouts-tv.html
+next.title=Handling features not supported on TV
+next.link=unsupported-features-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#HandleDpadNavigation">Handle D-pad Navigation</a></li>
+  <li><a href="#HandleFocusSelection">Provide Clear Visual Indication for Focus and Selection</a></li>
+  <li><a href="#DesignForEasyNavigation">Design for Easy Navigation</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+An important aspect of the user experience when operating a TV is the direct human interface: a remote control. 
+As you optimize your Android application for TVs, you should pay special attention to how the user actually navigates 
+around your application when using a remote control instead of a touchscreen.
+</p>
+<p>
+This lesson shows you how to optimize navigation for TV by:
+</p>
+
+<ul>
+  <li>Ensuring all layout controls are D-pad navigable.</li>
+  <li>Providing highly obvious feedback for UI navigation.</li>
+  <li>Placing layout controls for easy access.</li>
+</ul>
+
+<h2 id="HandleDpadNavigation">Handle D-pad Navigation</h2> 
+
+<p>
+On a TV, users navigate with controls on a TV remote, using either a D-pad or arrow keys. 
+This limits movement to up, down, left, and right. 
+To build a great TV-optimized app, you must provide a navigation scheme in which the user can 
+quickly learn how to navigate your app using the remote.
+</p>
+
+<p>
+When you design navigation for D-pad, follow these guidelines:
+</p>
+
+<ul>
+  <li>Ensure that the D-pad  can navigate to all the visible controls on the screen.</li>
+  <li>For scrolling lists with focus, D-pad up/down keys scroll the list and Enter key selects an item in the list. Ensure that users can 
+  select an element in the list and that the list still scrolls when an element is selected.</li> 
+  <li>Ensure that movement between controls is straightforward and predictable.</li>
+</ul>
+
+<p>
+Android usually handles navigation order between layout elements automatically, so you don't need to do anything extra. If the screen layout 
+makes navigation difficult, or if you want users to move through the layout in a specific way, you can set up explicit navigation for your 
+controls.  
+For example, for an {@code android.widget.EditText}, to define the next control to receive focus, use:
+<pre>
+&lt;EditText android:id="@+id/LastNameField" android:nextFocusDown="@+id/FirstNameField"\&gt;
+</pre>
+The following table lists all of the available navigation attributes:
+</p>
+
+<table>
+<tr>
+<th>Attribute</th>
+<th>Function</th>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusDown}</td>
+<td>Defines the next view to receive focus when the user navigates down.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusLeft}</td>
+<td>Defines the next view to receive focus when the user navigates left.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusRight}</td>
+<td>Defines the next view to receive focus when the user navigates right.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusUp}</td>
+<td>Defines the next view to receive focus when the user navigates up.</td>
+</tr>
+</table>
+
+<p>
+To use one of these explicit navigation attributes, set the value to the ID (android:id value) of another widget in the layout. You should set 
+up the navigation order as a loop, so that the last control directs focus back to the first one.
+</p>
+
+<p>
+Note: You should only use these attributes to modify the navigation order if the default order that the system applies does not work well.
+</p>
+
+<h2 id="HandleFocusSelection">Provide Clear Visual Indication for Focus and Selection</h2>
+
+<p>
+Use appropriate color highlights for all navigable and selectable elements in the UI. This makes it easy for users to know whether the control 
+is currently focused or selected when they navigate with a D-pad. Also, use uniform highlight scheme across your application.
+</p>
+
+<p>
+Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">Drawable State List Resources</a> to implement highlights 
+for selected and focused controls. For example:
+</p>
+
+res/drawable/button.xml:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;item android:state_pressed="true"
+          android:drawable="@drawable/button_pressed" /&gt; &lt;!-- pressed --&gt;
+    &lt;item android:state_focused="true"
+          android:drawable="@drawable/button_focused" /&gt; &lt;!-- focused --&gt;
+    &lt;item android:state_hovered="true"
+          android:drawable="@drawable/button_focused" /&gt; &lt;!-- hovered --&gt;
+    &lt;item android:drawable="@drawable/button_normal" /&gt; &lt;!-- default --&gt;
+&lt;/selector&gt;
+</pre>
+
+<p>
+This layout XML applies the above state list drawable to a {@link android.widget.Button}:
+</p>
+<pre>
+&lt;Button
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:background="@drawable/button" /&gt;
+</pre>
+
+<p>
+Provide sufficient padding within the focusable and selectable controls so that the highlights around them are clearly visible. 
+</p>
+
+<h2 id="DesignForEasyNavigation">Design for Easy Navigation</h2>
+
+<p>
+Users should be able to navigate to any UI control with a couple of D-pad clicks. Navigation should be easy and  intuitive to 
+understand.  For any non-intuitive actions, provide users with written help, using a dialog triggered by a help button or action bar icon. 
+</p>
+
+<p>
+Predict the next screen that the user will want to navigate to and provide one click navigation to it. If the current screen UI is very sparse, 
+consider making it a multi pane screen. Use fragments for making multi-pane screens. For example, consider the multi-pane UI below with continent names 
+on the left and list of cool places in each continent on the right. 
+</p>
+
+<img src="{@docRoot}images/training/cool-places.png" alt="" />
+
+<p>
+The above UI consists of three Fragments - <code>left_side_action_controls</code>, <code>continents</code> and 
+<code>places</code> - as shown in its layout 
+xml file below. Such multi-pane UIs make D-pad navigation easier and make good use of the horizontal screen space for 
+TVs.
+</p>
+res/layout/cool_places.xml
+<pre>
+&lt;LinearLayout   
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+   &gt;
+   &lt;fragment
+        android:id="@+id/left_side_action_controls"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_weight="0.2"/&gt;
+    &lt;fragment
+        android:id="@+id/continents"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_weight="0.2"/&gt;
+
+    &lt;fragment
+        android:id="@+id/places"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_weight="0.6"/&gt;
+
+&lt;/LinearLayout&gt;
+</pre>
+
+<p>
+Also, notice in the UI layout above action controls are on the left hand side of a vertically scrolling list to make 
+them easily accessible using D-pad. 
+In general, for layouts with horizontally scrolling components, place action controls on left or right hand side and 
+vice versa for vertically scrolling components.
+</p>
+
diff --git a/docs/html/training/tv/unsupported-features-tv.jd b/docs/html/training/tv/unsupported-features-tv.jd
new file mode 100644
index 0000000..6b0f8c8
--- /dev/null
+++ b/docs/html/training/tv/unsupported-features-tv.jd
@@ -0,0 +1,156 @@
+page.title=Handling Features Not Supported on TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Navigation for TV
+previous.link=optimizing-navigation-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#WorkaroundUnsupportedFeatures">Work Around Features Not Supported on TV</a></li>
+  <li><a href="#CheckAvailableFeatures">Check for Available Features at Runtime</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>
+TVs are much different from other Android-powered devices:
+</p>
+<ul>
+  <li>They're not mobile.</li>
+  <li>Out of habit, people use them for watching media with little or no interaction.</li>
+  <li>People interact with them from a distance.</li>
+</ul>
+
+<p>
+Because TVs have a different purpose from other devices, they usually don't have hardware features 
+that other Android-powered devices often have. For this reason, the Android system does not 
+support the following features for a TV device:
+<table>
+<tr>
+<th>Hardware</th>
+<th>Android feature descriptor</th>
+</tr>
+<tr>
+<td>Camera</td>
+<td>android.hardware.camera</td>
+</tr>
+<tr>
+<td>GPS</td>
+<td>android.hardware.location.gps</td>
+</tr>
+<tr>
+<td>Microphone</td>
+<td>android.hardware.microphone</td>
+</tr>
+<tr>
+<td>Near Field Communications (NFC)</td>
+<td>android.hardware.nfc</td>
+</tr>
+<tr>
+<td>Telephony</td>
+<td>android.hardware.telephony</td>
+</tr>
+<tr>
+<td>Touchscreen</td>
+<td>android.hardware.touchscreen</td>
+</tr>
+</table>
+</p>
+
+<p>
+This lesson shows you how to work around features that are not available on TV by:
+<ul>
+  <li>Providing work arounds for some non-supported features.</li>
+  <li>Checking for available features at runtime and conditionally activating/deactivating certain code 
+  paths based on availability of those features.</li>
+</ul>
+</p>
+
+
+<h2 id="WorkaroundUnsupportedFeatures">Work Around Features Not Supported on TV</h2> 
+
+<p>
+Android doesn't support touchscreen interaction for TV devices, most TVs don't have touch screens, 
+and interacting with a TV using a touchscreen is not consistent with the 10 foot environment. For 
+these reasons, users interact with Android-powered TVs using a remote. In consideration of this, 
+ensure that every control in your app can be accessed with the D-pad. Refer back to the previous two lessons 
+<a href="{@docRoot}training/tv/optimizing-layouts-tv">Optimizing Layouts for TV</a> and 
+<a href="{@docRoot}training/tv/optimizing-navigation-tv">Optimize Navigation for TV</a> for more details 
+on this topic. The Android system assumes that a device has a touchscreen, so if you want your application 
+to run on a TV, you must <strong>explicitly</strong> disable the touchscreen requirement in your manifest file:
+<pre>
+&lt;uses-feature android:name="android.hardware.touchscreen" android:required="false"/&gt;
+</pre>
+</p> 
+
+<p>
+Although a TV doesn't have a camera, you can still provide a photography-related application on a TV. 
+For example, if you have an app that takes, views and edits photos, you can disable its picture-taking 
+functionality for TVs and still allow users to view and even edit photos. The next section talks about how to 
+deactivate or activate specific functions in the application based on runtime device type detection.
+</p>
+
+<p>
+Because TVs are stationary, indoor devices, they don't have built-in GPS. If your application uses location 
+information, allow users to search for a location or use a "static" location provider to get 
+a location from the zip code configured during the TV setup.
+<pre>
+LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+Location location = locationManager.getLastKnownLocation("static");
+Geocoder geocoder = new Geocoder(this);
+Address address = null;
+
+try {
+  address = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1).get(0);
+  Log.d("Zip code", address.getPostalCode());
+
+} catch (IOException e) {
+  Log.e(TAG, "Geocoder error", e);
+}
+</pre>
+</p>
+
+<p>
+TVs usually don't support microphones, but if you have an application that uses voice control, 
+you can create a mobile device app that takes voice input and then acts as a remote control for a TV.
+</p>
+
+<h2 id="CheckAvailableFeatures">Check for Available Features at Runtime</h2>
+
+<p>
+To check if a feature is available at runtime, call 
+{@link android.content.pm.PackageManager#hasSystemFeature(String)}.
+ This method takes a single argument : a string corresponding to the 
+feature you want to check. For example, to check for touchscreen, use 
+{@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument 
+{@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.
+</p>
+
+<p>
+The following code snippet demonstrates how to detect device type at runtime based on supported features:
+
+<pre>
+// Check if android.hardware.telephony feature is available.
+if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
+   Log.d("Mobile Test", "Running on phone");
+// Check if android.hardware.touchscreen feature is available.
+} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
+   Log.d("Tablet Test", "Running on devices that don't support telphony but have a touchscreen.");
+} else {
+    Log.d("TV Test", "Running on a TV!");
+}
+</pre>
+</p>
+
+<p>
+This is just one example of using runtime checks to deactivate app functionality that depends on features 
+that aren't available on TVs.
+</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index dcda67d..7e92973 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -40,14 +40,8 @@
     // assigned in constructors, freed in finalizer
     final int mNativeCanvas;
     
-    /*  Our native canvas can be either a raster, gl, or picture canvas.
-        If we are raster, then mGL will be null, and mBitmap may or may not be
-        present (our default constructor creates a raster canvas but no
-        java-bitmap is). If we are a gl-based, then mBitmap will be null, and
-        mGL will not be null. Thus both cannot be non-null, but its possible
-        for both to be null.
-    */
-    private Bitmap  mBitmap;    // if not null, mGL must be null
+    // may be null
+    private Bitmap mBitmap;
     
     // optional field set by the caller
     private DrawFilter mDrawFilter;
@@ -66,7 +60,7 @@
     
     // Used by native code
     @SuppressWarnings({"UnusedDeclaration"})
-    private int         mSurfaceFormat;
+    private int mSurfaceFormat;
 
     /**
      * Flag for drawTextRun indicating left-to-right run direction.
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ec9b56b..39d2e39 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -631,6 +631,9 @@
     const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
 
     if (fboLayer) {
+        // Detach the texture from the FBO
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+
         // Unbind current FBO and restore previous one
         glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
     }
@@ -671,11 +674,6 @@
         //       code path
         // See LayerRenderer::destroyLayer(Layer*)
 
-        // Detach the texture from the FBO
-        glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
-        glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
-
         // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed
         mCaches.fboCache.put(current->fbo);
         layer->setFbo(0);
@@ -1330,18 +1328,18 @@
  * are set up for each individual segment.
  */
 void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
-        GLvoid* lengthCoords, float boundaryWidthProportion) {
+        GLvoid* lengthCoords, float boundaryWidthProportion, int& widthSlot, int& lengthSlot) {
     bool force = mCaches.unbindMeshBuffer();
     mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
             vertices, gAAVertexStride);
     mCaches.resetTexCoordsVertexPointer();
     mCaches.unbindIndicesBuffer();
 
-    int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
+    widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
     glEnableVertexAttribArray(widthSlot);
     glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
 
-    int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
+    lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
     glEnableVertexAttribArray(lengthSlot);
     glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
 
@@ -1350,7 +1348,12 @@
 
     // Setting the inverse value saves computations per-fragment in the shader
     int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
-    glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
+    glUniform1f(inverseBoundaryWidthSlot, 1.0f / boundaryWidthProportion);
+}
+
+void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
+    glDisableVertexAttribArray(widthSlot);
+    glDisableVertexAttribArray(lengthSlot);
 }
 
 void OpenGLRenderer::finishDrawTexture() {
@@ -1722,13 +1725,18 @@
     float width = right - left;
     float height = bottom - top;
 
+    int widthSlot;
+    int lengthSlot;
+
     float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
     float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
-    setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+    setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
+            boundaryWidthProportion, widthSlot, lengthSlot);
+
     int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
     int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
     glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
-    glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryHeightProportion));
+    glUniform1f(inverseBoundaryLengthSlot, (1.0f / boundaryHeightProportion));
 
     if (!quickReject(left, top, right, bottom)) {
         AAVertex::set(aaVertices++, left, bottom, 1, 1);
@@ -1738,6 +1746,8 @@
         dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     }
+
+    finishDrawAALine(widthSlot, lengthSlot);
 }
 
 /**
@@ -1768,11 +1778,14 @@
     // A stroke width of 0 has a special meaning in Skia:
     // it draws a line 1 px wide regardless of current transform
     bool isHairLine = paint->getStrokeWidth() == 0.0f;
+
     float inverseScaleX = 1.0f;
     float inverseScaleY = 1.0f;
     bool scaled = false;
+
     int alpha;
     SkXfermode::Mode mode;
+
     int generatedVerticesCount = 0;
     int verticesCount = count;
     if (count > 4) {
@@ -1792,10 +1805,13 @@
             float m10 = mat->data[Matrix4::kSkewX];
             float m11 = mat->data[Matrix4::kScaleX];
             float m12 = mat->data[6];
+
             float scaleX = sqrtf(m00 * m00 + m01 * m01);
             float scaleY = sqrtf(m10 * m10 + m11 * m11);
+
             inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
             inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+
             if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
                 scaled = true;
             }
@@ -1825,10 +1841,16 @@
         // Expand boundary to enable AA calculations on the quad border
         halfStrokeWidth += .5f;
     }
+
+    int widthSlot;
+    int lengthSlot;
+
     Vertex lines[verticesCount];
     Vertex* vertices = &lines[0];
+
     AAVertex wLines[verticesCount];
     AAVertex* aaVertices = &wLines[0];
+
     if (CC_UNLIKELY(!isAA)) {
         setupDrawVertices(vertices);
     } else {
@@ -1840,7 +1862,8 @@
         // We will need to calculate the actual width proportion on each segment for
         // scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
         float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
-        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
+                boundaryWidthProportion, widthSlot, lengthSlot);
     }
 
     AAVertex* prevAAVertex = NULL;
@@ -1850,10 +1873,12 @@
     int inverseBoundaryLengthSlot = -1;
     int boundaryWidthSlot = -1;
     int inverseBoundaryWidthSlot = -1;
+
     for (int i = 0; i < count; i += 4) {
         // a = start point, b = end point
         vec2 a(points[i], points[i + 1]);
         vec2 b(points[i + 2], points[i + 3]);
+
         float length = 0;
         float boundaryLengthProportion = 0;
         float boundaryWidthProportion = 0;
@@ -1870,6 +1895,7 @@
                 }
                 n *= wideningFactor;
             }
+
             if (scaled) {
                 n.x *= inverseScaleX;
                 n.y *= inverseScaleY;
@@ -1880,11 +1906,13 @@
             extendedN /= 2;
             extendedN.x *= inverseScaleX;
             extendedN.y *= inverseScaleY;
+
             float extendedNLength = extendedN.length();
             // We need to set this value on the shader prior to drawing
             boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
             n += extendedN;
         }
+
         float x = n.x;
         n.x = -n.y;
         n.y = x;
@@ -1894,6 +1922,7 @@
             vec2 abVector = (b - a);
             length = abVector.length();
             abVector.normalize();
+
             if (scaled) {
                 abVector.x *= inverseScaleX;
                 abVector.y *= inverseScaleY;
@@ -1902,6 +1931,7 @@
             } else {
                 boundaryLengthProportion = .5 / (length + 1);
             }
+
             abVector /= 2;
             a -= abVector;
             b += abVector;
@@ -1930,10 +1960,12 @@
                     Vertex::set(vertices++, p1.x, p1.y);
                     generatedVerticesCount += 2;
                 }
+
                 Vertex::set(vertices++, p1.x, p1.y);
                 Vertex::set(vertices++, p2.x, p2.y);
                 Vertex::set(vertices++, p4.x, p4.y);
                 Vertex::set(vertices++, p3.x, p3.y);
+
                 prevVertex = vertices - 1;
                 generatedVerticesCount += 4;
             } else {
@@ -1946,14 +1978,17 @@
                         inverseBoundaryWidthSlot =
                                 mCaches.currentProgram->getUniform("inverseBoundaryWidth");
                     }
+
                     glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
                     glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
                 }
+
                 if (boundaryLengthSlot < 0) {
                     boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
                     inverseBoundaryLengthSlot =
                             mCaches.currentProgram->getUniform("inverseBoundaryLength");
                 }
+
                 glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
                 glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
 
@@ -1967,21 +2002,29 @@
                     AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
                     generatedVerticesCount += 2;
                 }
+
                 AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
                 AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
                 AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
                 AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
+
                 prevAAVertex = aaVertices - 1;
                 generatedVerticesCount += 4;
             }
+
             dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
                     a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
                     *mSnapshot->transform);
         }
     }
+
     if (generatedVerticesCount > 0) {
        glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
     }
+
+    if (isAA) {
+        finishDrawAALine(widthSlot, lengthSlot);
+    }
 }
 
 void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
@@ -2027,10 +2070,12 @@
     for (int i = 0; i < count; i += 2) {
         TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
         generatedVerticesCount++;
+
         float left = points[i] - halfWidth;
         float right = points[i] + halfWidth;
         float top = points[i + 1] - halfWidth;
         float bottom = points [i + 1] + halfWidth;
+
         dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index ab137cc..b52d2b0 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -553,7 +553,8 @@
     void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
     void setupDrawVertices(GLvoid* vertices);
     void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
-            float strokeWidth);
+            float strokeWidth, int& widthSlot, int& lengthSlot);
+    void finishDrawAALine(const int widthSlot, const int lengthSlot);
     void finishDrawTexture();
     void accountForClear(SkXfermode::Mode mode);
 
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index dd5e026..b698705 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -4,9 +4,9 @@
      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.
@@ -16,7 +16,7 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.mediaframeworktest">
-    
+
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.INTERNET" />
@@ -24,7 +24,7 @@
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
 
-    <application>    
+    <application>
         <uses-library android:name="android.test.runner" />
         <activity android:label="@string/app_name"
                 android:name="MediaFrameworkTest"
@@ -34,12 +34,18 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
-    </application>   
+    </application>
+
+    <instrumentation android:name=".CameraStressTestRunner"
+        android:targetPackage="com.android.mediaframeworktest"
+        android:label="Camera stress tests InstrumentationRunner">
+    </instrumentation>
+
     <instrumentation android:name=".MediaFrameworkTestRunner"
          android:targetPackage="com.android.mediaframeworktest"
          android:label="MediaFramework tests InstrumentationRunner">
      </instrumentation>
-     
+
       <instrumentation android:name=".MediaFrameworkPerfTestRunner"
          android:targetPackage="com.android.mediaframeworktest"
          android:label="MediaFramework Performance tests InstrumentationRunner">
@@ -49,7 +55,7 @@
          android:targetPackage="com.android.mediaframeworktest"
          android:label="MediaFramework unit tests InstrumentationRunner">
      </instrumentation>
-     
+
      <instrumentation android:name=".MediaRecorderStressTestRunner"
          android:targetPackage="com.android.mediaframeworktest"
          android:label="MediaRecorder stress tests InstrumentationRunner">
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/CameraStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/CameraStressTestRunner.java
new file mode 100644
index 0000000..fa59fa4
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/CameraStressTestRunner.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.mediaframeworktest;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import com.android.mediaframeworktest.stress.CameraStressTest;
+
+import junit.framework.TestSuite;
+
+public class CameraStressTestRunner extends InstrumentationTestRunner {
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(CameraStressTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return CameraStressTestRunner.class.getClassLoader();
+    }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
new file mode 100644
index 0000000..a9c6119
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 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.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.FileWriter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.Camera;
+import android.hardware.Camera.PictureCallback;
+import android.hardware.Camera.ShutterCallback;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import com.android.mediaframeworktest.CameraStressTestRunner;
+
+import junit.framework.Assert;
+
+/**
+ * Junit / Instrumentation test case for the camera zoom api
+ *
+ * adb shell am instrument
+ *  -e class com.android.mediaframeworktest.stress.CameraStressTest
+ *  -w com.android.mediaframeworktest/.CameraStressTestRunner
+ */
+public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "CameraStressTest";
+    private Camera mCamera;
+
+    private static final int NUMBER_OF_ZOOM_LOOPS = 100;
+    private static final long WAIT_GENERIC = 3 * 1000; // 3 seconds
+    private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
+    private static final long WAIT_ZOOM_ANIMATION = 5 * 1000; // 5 seconds
+    private static final String CAMERA_STRESS_OUTPUT =
+            "/sdcard/cameraStressOutput.txt";
+    private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
+
+    private Thread mLooperThread;
+    private Handler mHandler;
+
+    public CameraStressTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    protected void setUp() throws Exception {
+        final Semaphore sem = new Semaphore(0);
+        mLooperThread = new Thread() {
+            @Override
+            public void run() {
+                Log.v(TAG, "starting looper");
+                Looper.prepare();
+                mHandler = new Handler();
+                sem.release();
+                Looper.loop();
+                Log.v(TAG, "quit looper");
+            }
+        };
+        mLooperThread.start();
+        if (!sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
+            fail("Failed to start the looper.");
+        }
+        getActivity();
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHandler != null) {
+            mHandler.getLooper().quit();
+            mHandler = null;
+        }
+        if (mLooperThread != null) {
+            mLooperThread.join(WAIT_TIMEOUT);
+            if (mLooperThread.isAlive()) {
+                fail("Failed to stop the looper.");
+            }
+            mLooperThread = null;
+        }
+
+        super.tearDown();
+    }
+
+    private void runOnLooper(final Runnable command) throws InterruptedException {
+        final Semaphore sem = new Semaphore(0);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    command.run();
+                } finally {
+                    sem.release();
+                }
+            }
+        });
+        if (!sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
+            fail("Failed to run the command on the looper.");
+        }
+    }
+
+    private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
+        public void onError(int error, android.hardware.Camera camera) {
+            if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
+                assertTrue("Camera test mediaserver died", false);
+            }
+        }
+    }
+
+    private ShutterCallback shutterCallback = new ShutterCallback() {
+        @Override
+        public void onShutter() {
+            Log.v(TAG, "Shutter");
+        }
+    };
+
+    private PictureCallback rawCallback = new PictureCallback() {
+        @Override
+        public void onPictureTaken(byte[] data, Camera camera) {
+            Log.v(TAG, "Raw picture taken");
+        }
+    };
+
+    private PictureCallback jpegCallback = new PictureCallback() {
+        @Override
+        public void onPictureTaken(byte[] data, Camera camera) {
+            FileOutputStream fos = null;
+
+            try {
+                Log.v(TAG, "JPEG picture taken");
+                fos = new FileOutputStream(String.format("%s/zoom-test-%d.jpg",
+                        Environment.getExternalStorageDirectory(), System.currentTimeMillis()));
+                fos.write(data);
+            }
+            catch (FileNotFoundException e) {
+                Log.v(TAG, "File not found: " + e.toString());
+            }
+            catch (IOException e) {
+                Log.v(TAG, "Error accessing file: " + e.toString());
+            }
+            finally {
+                try {
+                    if (fos != null) {
+                        fos.close();
+                    }
+                }
+                catch (IOException e) {
+                    Log.v(TAG, "Error closing file: " + e.toString());
+                }
+            }
+        }
+    };
+
+    // Helper method for cleaning up pics taken during testStressCameraZoom
+    private void cleanupZoomImages() {
+        try {
+            File sdcard = Environment.getExternalStorageDirectory();
+            File[] zoomImages = null;
+
+            FilenameFilter filter = new FilenameFilter() {
+                public boolean accept(File dir, String name) {
+                    return name.startsWith("zoom-test-");
+                }
+            };
+
+            zoomImages = sdcard.listFiles(filter);
+
+            for (File f : zoomImages) {
+                f.delete();
+            }
+        }
+        catch (SecurityException e) {
+            Log.v(TAG, "Security manager access violation: " + e.toString());
+        }
+    }
+
+    // Test case for stressing the camera zoom in/out feature
+    @LargeTest
+    public void testStressCameraZoom() throws Exception {
+        SurfaceHolder mSurfaceHolder;
+        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+        File stressOutFile = new File(CAMERA_STRESS_OUTPUT);
+        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
+        output.write("Camera zoom stress:\n");
+        output.write("Total number of loops: " +  NUMBER_OF_ZOOM_LOOPS + "\n");
+
+        try {
+            Log.v(TAG, "Start preview");
+            output.write("No of loop: ");
+
+            mCamera = Camera.open();
+            Camera.Parameters params = mCamera.getParameters();
+            mCamera.release();
+
+            if (!params.isSmoothZoomSupported() && !params.isZoomSupported()) {
+                Log.v(TAG, "Device camera does not support zoom");
+                assertTrue("Camera zoom stress test", false);
+            }
+            else {
+                Log.v(TAG, "Device camera does support zoom");
+
+                int nextZoomLevel = 0;
+
+                for (int i = 0; i < NUMBER_OF_ZOOM_LOOPS; i++) {
+                    runOnLooper(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCamera = Camera.open();
+                        }
+                    });
+
+                    mCamera.setErrorCallback(mCameraErrorCallback);
+                    mCamera.setPreviewDisplay(mSurfaceHolder);
+                    mCamera.startPreview();
+                    Thread.sleep(WAIT_GENERIC);
+
+                    params = mCamera.getParameters();
+                    int currentZoomLevel = params.getZoom();
+
+                    if (nextZoomLevel >= params.getMaxZoom()) {
+                        nextZoomLevel = 0;
+                    }
+                    ++nextZoomLevel;
+
+                    if (params.isSmoothZoomSupported()) {
+                        mCamera.startSmoothZoom(nextZoomLevel);
+                    }
+                    else {
+                        params.setZoom(nextZoomLevel);
+                        mCamera.setParameters(params);
+                    }
+                    Log.v(TAG, "Zooming from " + currentZoomLevel + " to " + nextZoomLevel);
+
+                    // sleep allows for zoom animation to finish
+                    Thread.sleep(WAIT_ZOOM_ANIMATION);
+
+                    // take picture
+                    mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
+                    Thread.sleep(WAIT_GENERIC);
+                    mCamera.stopPreview();
+                    mCamera.release();
+                    output.write(" ," + i);
+                }
+            }
+
+            cleanupZoomImages();
+        }
+        catch (Exception e) {
+            assertTrue("Camera zoom stress test Exception", false);
+            Log.v(TAG, e.toString());
+        }
+        output.write("\n\n");
+        output.close();
+    }
+}
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index aa27861..715ccba 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -66,7 +66,7 @@
             android:layout_height="wrap_content"
             android:textSize="@dimen/status_bar_recents_app_label_text_size"
             android:fadingEdge="horizontal"
-            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
             android:scrollHorizontally="true"
             android:layout_alignLeft="@id/app_thumbnail"
             android:layout_below="@id/app_thumbnail"
@@ -82,7 +82,7 @@
             android:layout_height="wrap_content"
             android:textSize="@dimen/status_bar_recents_app_description_text_size"
             android:fadingEdge="horizontal"
-            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
             android:scrollHorizontally="true"
             android:layout_alignLeft="@id/app_thumbnail"
             android:layout_below="@id/app_label"
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index 180f022..dba1dd9 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -41,7 +41,7 @@
             android:stackFromBottom="true"
             android:fadingEdge="horizontal"
             android:scrollbars="none"
-            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length"
             android:layout_gravity="bottom|left"
             android:orientation="horizontal"
             android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index bc389f9..ca72530 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -35,7 +35,7 @@
             android:layout_height="wrap_content"
             android:textSize="@dimen/status_bar_recents_app_label_text_size"
             android:fadingEdge="horizontal"
-            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
             android:scrollHorizontally="true"
             android:layout_alignParentLeft="true"
             android:layout_alignTop="@id/app_icon"
@@ -89,7 +89,7 @@
             android:layout_height="wrap_content"
             android:textSize="@dimen/status_bar_recents_app_description_text_size"
             android:fadingEdge="horizontal"
-            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
             android:scrollHorizontally="true"
             android:layout_alignParentLeft="true"
             android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index e6a077a..73c3da5 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -40,7 +40,7 @@
             android:stackFromBottom="true"
             android:fadingEdge="vertical"
             android:scrollbars="none"
-            android:fadingEdgeLength="@*android:dimen/status_bar_height"
+            android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length"
             android:layout_gravity="bottom|left"
             android:clipToPadding="false"
             android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index 333fcda..7d639ec 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -28,7 +28,7 @@
         android:layout_height="wrap_content"
         android:textSize="@dimen/status_bar_recents_app_label_text_size"
         android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+        android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
         android:scrollHorizontally="true"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
@@ -86,7 +86,7 @@
         android:layout_height="wrap_content"
         android:textSize="@dimen/status_bar_recents_app_description_text_size"
         android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+        android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
         android:scrollHorizontally="true"
         android:layout_alignParentLeft="true"
         android:layout_below="@id/recents_callout_line"
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index ba1cdfa..8c1ac55 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -53,8 +53,10 @@
     <dimen name="status_bar_recents_app_label_width">97dip</dimen>
     <!-- Left margin for application label -->
     <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
-    <!-- Size of fading edge for scroll effect -->
-    <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+    <!-- Size of fading edge for text -->
+    <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
+    <!-- Size of fading edge for scrolling -->
+    <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
     <!-- Margin between recents container and glow on the right -->
     <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2c22e3c..4441ca6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -33,8 +33,10 @@
     <dimen name="status_bar_recents_app_label_text_size">14dip</dimen>
     <!-- Size of application description text -->
     <dimen name="status_bar_recents_app_description_text_size">14dip</dimen>
-    <!-- Size of fading edge for scroll effect -->
-    <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+    <!-- Size of fading edge for text -->
+    <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
+    <!-- Size of fading edge for scrolling -->
+    <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
     <!-- Margin between recents container and glow on the right -->
     <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
     <!-- Amount to offset bottom of notification peek window from top of status bar. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index ebed522..5d6e315 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -68,6 +68,7 @@
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
     private Context mContext;
     private BaseStatusBar mBar;
+    private PopupMenu mPopup;
     private View mRecentsScrim;
     private View mRecentsNoApps;
     private ViewGroup mRecentsContainer;
@@ -313,6 +314,10 @@
             setFocusable(true);
             setFocusableInTouchMode(true);
             requestFocus();
+        } else {
+            if (mPopup != null) {
+                mPopup.dismiss();
+            }
         }
     }
 
@@ -325,6 +330,7 @@
             setVisibility(View.GONE);
         }
         if (mBar != null) {
+            // This will indirectly cause show(false, ...) to get called
             mBar.animateCollapse();
         }
     }
@@ -729,10 +735,20 @@
         getContext().startActivity(intent);
     }
 
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mPopup != null) {
+            return true;
+        } else {
+            return super.onInterceptTouchEvent(ev);
+        }
+    }
+
     public void handleLongPress(
             final View selectedView, final View anchorView, final View thumbnailView) {
         thumbnailView.setSelected(true);
-        PopupMenu popup = new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
+        final PopupMenu popup =
+            new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
+        mPopup = popup;
         popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
         popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
             public boolean onMenuItemClick(MenuItem item) {
@@ -756,6 +772,7 @@
         popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
             public void onDismiss(PopupMenu menu) {
                 thumbnailView.setSelected(false);
+                mPopup = null;
             }
         });
         popup.show();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
index 5529d0c..6bd1826 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
@@ -37,15 +37,11 @@
     public static final boolean OPTIMIZE_SW_RENDERED_RECENTS = true;
     public static final boolean USE_DARK_FADE_IN_HW_ACCELERATED_MODE = true;
     private View mScrollView;
-    private LinearLayout mLinearLayout;
     private RecentsCallback mCallback;
 
-    private boolean mShowBackground = false;
     private int mFadingEdgeLength;
-    private Drawable.ConstantState mBackgroundDrawable;
     private Context mContext;
     private boolean mIsVertical;
-    private boolean mFirstTime = true;
     private boolean mSoftwareRendered = false;
     private boolean mAttachedToWindow = false;
 
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
new file mode 100644
index 0000000..6ba4c35
--- /dev/null
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -0,0 +1,288 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.server.wm;
+
+import android.graphics.Matrix;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+public class AppWindowAnimator {
+
+    final AppWindowToken mAppToken;
+    final WindowManagerService mService;
+    final WindowAnimator mAnimator;
+
+    boolean animating;
+    Animation animation;
+    boolean animInitialized;
+    boolean hasTransformation;
+    final Transformation transformation = new Transformation();
+
+    // Have we been asked to have this token keep the screen frozen?
+    // Protect with mAnimator.
+    boolean freezingScreen;
+
+    // Offset to the window of all layers in the token, for use by
+    // AppWindowToken animations.
+    int animLayerAdjustment;
+
+    // Special surface for thumbnail animation.
+    Surface thumbnail;
+    int thumbnailTransactionSeq;
+    int thumbnailX;
+    int thumbnailY;
+    int thumbnailLayer;
+    Animation thumbnailAnimation;
+    final Transformation thumbnailTransformation = new Transformation();
+
+    public AppWindowAnimator(final WindowManagerService service, final AppWindowToken atoken) {
+        mService = service;
+        mAppToken = atoken;
+        mAnimator = service.mAnimator;
+    }
+
+    public void setAnimation(Animation anim, boolean initialized) {
+        if (WindowManagerService.localLOGV) Slog.v(
+            WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+        animation = anim;
+        animating = false;
+        animInitialized = initialized;
+        anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
+        anim.scaleCurrentDuration(mService.mTransitionAnimationScale);
+        int zorder = anim.getZAdjustment();
+        int adj = 0;
+        if (zorder == Animation.ZORDER_TOP) {
+            adj = WindowManagerService.TYPE_LAYER_OFFSET;
+        } else if (zorder == Animation.ZORDER_BOTTOM) {
+            adj = -WindowManagerService.TYPE_LAYER_OFFSET;
+        }
+
+        if (animLayerAdjustment != adj) {
+            animLayerAdjustment = adj;
+            updateLayers();
+        }
+        // Start out animation gone if window is gone, or visible if window is visible.
+        transformation.clear();
+        transformation.setAlpha(mAppToken.reportedVisible ? 1 : 0);
+        hasTransformation = true;
+    }
+
+    public void setDummyAnimation() {
+        if (animation == null) {
+            if (WindowManagerService.localLOGV) Slog.v(
+                WindowManagerService.TAG, "Setting dummy animation in " + this);
+            animation = WindowManagerService.sDummyAnimation;
+            animInitialized = false;
+        }
+    }
+
+    public void clearAnimation() {
+        if (animation != null) {
+            animation = null;
+            animating = true;
+            animInitialized = false;
+        }
+        clearThumbnail();
+    }
+
+    public void clearThumbnail() {
+        if (thumbnail != null) {
+            thumbnail.destroy();
+            thumbnail = null;
+        }
+    }
+
+    void updateLayers() {
+        final int N = mAppToken.allAppWindows.size();
+        final int adj = animLayerAdjustment;
+        thumbnailLayer = -1;
+        for (int i=0; i<N; i++) {
+            final WindowState w = mAppToken.allAppWindows.get(i);
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+            winAnimator.mAnimLayer = w.mLayer + adj;
+            if (winAnimator.mAnimLayer > thumbnailLayer) {
+                thumbnailLayer = winAnimator.mAnimLayer;
+            }
+            if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
+                    + winAnimator.mAnimLayer);
+            if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
+                mService.setInputMethodAnimLayerAdjustment(adj);
+            }
+            if (w == mService.mWallpaperTarget && mService.mLowerWallpaperTarget == null) {
+                mService.setWallpaperAnimLayerAdjustmentLocked(adj);
+            }
+        }
+    }
+
+    private void stepThumbnailAnimation(long currentTime) {
+        thumbnailTransformation.clear();
+        thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
+        thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
+        final boolean screenAnimation = mAnimator.mScreenRotationAnimation != null
+                && mAnimator.mScreenRotationAnimation.isAnimating();
+        if (screenAnimation) {
+            thumbnailTransformation.postCompose(
+                    mAnimator.mScreenRotationAnimation.getEnterTransformation());
+        }
+        // cache often used attributes locally
+        final float tmpFloats[] = mService.mTmpFloats;
+        thumbnailTransformation.getMatrix().getValues(tmpFloats);
+        if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
+                "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
+                + ", " + tmpFloats[Matrix.MTRANS_Y], null);
+        thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
+        if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
+                "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
+                + " layer=" + thumbnailLayer
+                + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+                + "," + tmpFloats[Matrix.MSKEW_Y]
+                + "][" + tmpFloats[Matrix.MSKEW_X]
+                + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
+        thumbnail.setAlpha(thumbnailTransformation.getAlpha());
+        // The thumbnail is layered below the window immediately above this
+        // token's anim layer.
+        thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+                - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+        thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
+                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+    }
+
+    private boolean stepAnimation(long currentTime) {
+        if (animation == null) {
+            return false;
+        }
+        transformation.clear();
+        final boolean more = animation.getTransformation(currentTime, transformation);
+        if (WindowManagerService.DEBUG_ANIM) Slog.v(
+            WindowManagerService.TAG, "Stepped animation in " + this +
+            ": more=" + more + ", xform=" + transformation);
+        if (!more) {
+            animation = null;
+            clearThumbnail();
+            if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                WindowManagerService.TAG, "Finished animation in " + this +
+                " @ " + currentTime);
+        }
+        hasTransformation = more;
+        return more;
+    }
+
+    // This must be called while inside a transaction.
+    boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+        if (mService.okToDisplay()) {
+            // We will run animations as long as the display isn't frozen.
+
+            if (animation == WindowManagerService.sDummyAnimation) {
+                // This guy is going to animate, but not yet.  For now count
+                // it as not animating for purposes of scheduling transactions;
+                // when it is really time to animate, this will be set to
+                // a real animation and the next call will execute normally.
+                return false;
+            }
+
+            if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
+                    && animation != null) {
+                if (!animating) {
+                    if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                        WindowManagerService.TAG, "Starting animation in " + this +
+                        " @ " + currentTime + ": dw=" + dw + " dh=" + dh
+                        + " scale=" + mService.mTransitionAnimationScale
+                        + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
+                    if (!animInitialized) {
+                        animation.initialize(dw, dh, dw, dh);
+                    }
+                    animation.setStartTime(currentTime);
+                    animating = true;
+                    if (thumbnail != null) {
+                        thumbnail.show();
+                        thumbnailAnimation.setStartTime(currentTime);
+                    }
+                }
+                if (stepAnimation(currentTime)) {
+                    // animation isn't over, step any thumbnail and that's
+                    // it for now.
+                    if (thumbnail != null) {
+                        stepThumbnailAnimation(currentTime);
+                    }
+                    return true;
+                }
+            }
+        } else if (animation != null) {
+            // If the display is frozen, and there is a pending animation,
+            // clear it and make sure we run the cleanup code.
+            animating = true;
+            animation = null;
+        }
+
+        hasTransformation = false;
+
+        if (!animating) {
+            return false;
+        }
+
+        mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+            mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
+        }
+
+        clearAnimation();
+        animating = false;
+        if (animLayerAdjustment != 0) {
+            animLayerAdjustment = 0;
+            updateLayers();
+        }
+        if (mService.mInputMethodTarget != null
+                && mService.mInputMethodTarget.mAppToken == mAppToken) {
+            mService.moveInputMethodWindowsIfNeededLocked(true);
+        }
+
+        if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                WindowManagerService.TAG, "Animation done in " + this
+                + ": reportedVisible=" + mAppToken.reportedVisible);
+
+        transformation.clear();
+
+        final int N = mAppToken.windows.size();
+        for (int i=0; i<N; i++) {
+            mAppToken.windows.get(i).mWinAnimator.finishExit();
+        }
+        mAppToken.updateReportedVisibilityLocked();
+
+        return false;
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        if (freezingScreen) {
+            pw.print(prefix); pw.print(" freezingScreen="); pw.println(freezingScreen);
+        }
+        if (animating || animation != null) {
+            pw.print(prefix); pw.print("animating="); pw.print(animating);
+                    pw.print(" animation="); pw.println(animation);
+        }
+        if (hasTransformation) {
+            pw.print(prefix); pw.print("XForm: ");
+                    transformation.printShortString(pw);
+                    pw.println();
+        }
+        if (animLayerAdjustment != 0) {
+            pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
+        }
+        if (thumbnail != null) {
+            pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
+                    pw.print(" x="); pw.print(thumbnailX);
+                    pw.print(" y="); pw.print(thumbnailY);
+                    pw.print(" layer="); pw.println(thumbnailLayer);
+            pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
+            pw.print(prefix); pw.print("thumbnailTransformation=");
+                    pw.println(thumbnailTransformation.toShortString());
+        }
+    }
+}
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 3069b74..e3d46d8 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -21,17 +21,12 @@
 import com.android.server.wm.WindowManagerService.H;
 
 import android.content.pm.ActivityInfo;
-import android.graphics.Matrix;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.IApplicationToken;
-import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -47,6 +42,9 @@
     // All of the windows and child windows that are included in this
     // application token.  Note this list is NOT sorted!
     final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
+    final AppWindowAnimator mAppAnimator;
+
+    final WindowAnimator mAnimator;
 
     int groupId = -1;
     boolean appFullscreen;
@@ -87,19 +85,6 @@
     // Set to true when the token has been removed from the window mgr.
     boolean removed;
 
-    // Have we been asked to have this token keep the screen frozen?
-    boolean freezingScreen;
-
-    boolean animating;
-    Animation animation;
-    boolean animInitialized;
-    boolean hasTransformation;
-    final Transformation transformation = new Transformation();
-
-    // Offset to the window of all layers in the token, for use by
-    // AppWindowToken animations.
-    int animLayerAdjustment;
-
     // Information about an application starting window if displayed.
     StartingData startingData;
     WindowState startingWindow;
@@ -108,15 +93,6 @@
     boolean startingMoved;
     boolean firstWindowDrawn;
 
-    // Special surface for thumbnail animation.
-    Surface thumbnail;
-    int thumbnailTransactionSeq;
-    int thumbnailX;
-    int thumbnailY;
-    int thumbnailLayer;
-    Animation thumbnailAnimation;
-    final Transformation thumbnailTransformation = new Transformation();
-
     // Input application handle used by the input dispatcher.
     final InputApplicationHandle mInputApplicationHandle;
 
@@ -126,79 +102,8 @@
         appWindowToken = this;
         appToken = _token;
         mInputApplicationHandle = new InputApplicationHandle(this);
-    }
-
-    public void setAnimation(Animation anim, boolean initialized) {
-        if (WindowManagerService.localLOGV) Slog.v(
-            WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
-        animation = anim;
-        animating = false;
-        animInitialized = initialized;
-        anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
-        anim.scaleCurrentDuration(service.mTransitionAnimationScale);
-        int zorder = anim.getZAdjustment();
-        int adj = 0;
-        if (zorder == Animation.ZORDER_TOP) {
-            adj = WindowManagerService.TYPE_LAYER_OFFSET;
-        } else if (zorder == Animation.ZORDER_BOTTOM) {
-            adj = -WindowManagerService.TYPE_LAYER_OFFSET;
-        }
-
-        if (animLayerAdjustment != adj) {
-            animLayerAdjustment = adj;
-            updateLayers();
-        }
-        // Start out animation gone if window is gone, or visible if window is visible.
-        transformation.clear();
-        transformation.setAlpha(reportedVisible ? 1 : 0);
-        hasTransformation = true;
-    }
-
-    public void setDummyAnimation() {
-        if (animation == null) {
-            if (WindowManagerService.localLOGV) Slog.v(
-                WindowManagerService.TAG, "Setting dummy animation in " + this);
-            animation = WindowManagerService.sDummyAnimation;
-            animInitialized = false;
-        }
-    }
-
-    public void clearAnimation() {
-        if (animation != null) {
-            animation = null;
-            animating = true;
-            animInitialized = false;
-        }
-        clearThumbnail();
-    }
-
-    public void clearThumbnail() {
-        if (thumbnail != null) {
-            thumbnail.destroy();
-            thumbnail = null;
-        }
-    }
-
-    void updateLayers() {
-        final int N = allAppWindows.size();
-        final int adj = animLayerAdjustment;
-        thumbnailLayer = -1;
-        for (int i=0; i<N; i++) {
-            final WindowState w = allAppWindows.get(i);
-            final WindowStateAnimator winAnimator = w.mWinAnimator;
-            winAnimator.mAnimLayer = w.mLayer + adj;
-            if (winAnimator.mAnimLayer > thumbnailLayer) {
-                thumbnailLayer = winAnimator.mAnimLayer;
-            }
-            if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
-                    + winAnimator.mAnimLayer);
-            if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
-                service.setInputMethodAnimLayerAdjustment(adj);
-            }
-            if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) {
-                service.setWallpaperAnimLayerAdjustmentLocked(adj);
-            }
-        }
+        mAnimator = service.mAnimator;
+        mAppAnimator = new AppWindowAnimator(_service, this);
     }
 
     void sendAppVisibilityToClients() {
@@ -231,141 +136,6 @@
         return isAnimating;
     }
 
-    private void stepThumbnailAnimation(long currentTime) {
-        thumbnailTransformation.clear();
-        thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
-        thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
-        final boolean screenAnimation = service.mAnimator.mScreenRotationAnimation != null
-                && service.mAnimator.mScreenRotationAnimation.isAnimating();
-        if (screenAnimation) {
-            thumbnailTransformation.postCompose(
-                    service.mAnimator.mScreenRotationAnimation.getEnterTransformation());
-        }
-        // cache often used attributes locally
-        final float tmpFloats[] = service.mTmpFloats;
-        thumbnailTransformation.getMatrix().getValues(tmpFloats);
-        if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
-                "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
-                + ", " + tmpFloats[Matrix.MTRANS_Y], null);
-        thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
-        if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
-                "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
-                + " layer=" + thumbnailLayer
-                + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
-                + "," + tmpFloats[Matrix.MSKEW_Y]
-                + "][" + tmpFloats[Matrix.MSKEW_X]
-                + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
-        thumbnail.setAlpha(thumbnailTransformation.getAlpha());
-        // The thumbnail is layered below the window immediately above this
-        // token's anim layer.
-        thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
-                - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
-        thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
-                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
-    }
-
-    private boolean stepAnimation(long currentTime) {
-        if (animation == null) {
-            return false;
-        }
-        transformation.clear();
-        final boolean more = animation.getTransformation(currentTime, transformation);
-        if (WindowManagerService.DEBUG_ANIM) Slog.v(
-            WindowManagerService.TAG, "Stepped animation in " + this +
-            ": more=" + more + ", xform=" + transformation);
-        if (!more) {
-            animation = null;
-            clearThumbnail();
-            if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                WindowManagerService.TAG, "Finished animation in " + this +
-                " @ " + currentTime);
-        }
-        hasTransformation = more;
-        return more;
-    }
-
-    // This must be called while inside a transaction.
-    boolean stepAnimationLocked(long currentTime, int dw, int dh) {
-        if (service.okToDisplay()) {
-            // We will run animations as long as the display isn't frozen.
-
-            if (animation == WindowManagerService.sDummyAnimation) {
-                // This guy is going to animate, but not yet.  For now count
-                // it as not animating for purposes of scheduling transactions;
-                // when it is really time to animate, this will be set to
-                // a real animation and the next call will execute normally.
-                return false;
-            }
-
-            if ((allDrawn || animating || startingDisplayed) && animation != null) {
-                if (!animating) {
-                    if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                        WindowManagerService.TAG, "Starting animation in " + this +
-                        " @ " + currentTime + ": dw=" + dw + " dh=" + dh
-                        + " scale=" + service.mTransitionAnimationScale
-                        + " allDrawn=" + allDrawn + " animating=" + animating);
-                    if (!animInitialized) {
-                        animation.initialize(dw, dh, dw, dh);
-                    }
-                    animation.setStartTime(currentTime);
-                    animating = true;
-                    if (thumbnail != null) {
-                        thumbnail.show();
-                        thumbnailAnimation.setStartTime(currentTime);
-                    }
-                }
-                if (stepAnimation(currentTime)) {
-                    // animation isn't over, step any thumbnail and that's
-                    // it for now.
-                    if (thumbnail != null) {
-                        stepThumbnailAnimation(currentTime);
-                    }
-                    return true;
-                }
-            }
-        } else if (animation != null) {
-            // If the display is frozen, and there is a pending animation,
-            // clear it and make sure we run the cleanup code.
-            animating = true;
-            animation = null;
-        }
-
-        hasTransformation = false;
-
-        if (!animating) {
-            return false;
-        }
-
-        service.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-            service.debugLayoutRepeats("AppWindowToken");
-        }
-
-        clearAnimation();
-        animating = false;
-        if (animLayerAdjustment != 0) {
-            animLayerAdjustment = 0;
-            updateLayers();
-        }
-        if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) {
-            service.moveInputMethodWindowsIfNeededLocked(true);
-        }
-
-        if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                WindowManagerService.TAG, "Animation done in " + this
-                + ": reportedVisible=" + reportedVisible);
-
-        transformation.clear();
-
-        final int N = windows.size();
-        for (int i=0; i<N; i++) {
-            windows.get(i).mWinAnimator.finishExit();
-        }
-        updateReportedVisibilityLocked();
-
-        return false;
-    }
-
     void updateReportedVisibilityLocked() {
         if (appToken == null) {
             return;
@@ -479,9 +249,8 @@
                 pw.print(" willBeHidden="); pw.print(willBeHidden);
                 pw.print(" reportedDrawn="); pw.print(reportedDrawn);
                 pw.print(" reportedVisible="); pw.println(reportedVisible);
-        if (paused || freezingScreen) {
-            pw.print(prefix); pw.print("paused="); pw.print(paused);
-                    pw.print(" freezingScreen="); pw.println(freezingScreen);
+        if (paused) {
+            pw.print(prefix); pw.print("paused="); pw.println(paused);
         }
         if (numInterestingWindows != 0 || numDrawnWindows != 0
                 || inPendingTransaction || allDrawn) {
@@ -491,18 +260,6 @@
                     pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
                     pw.print(" allDrawn="); pw.println(allDrawn);
         }
-        if (animating || animation != null) {
-            pw.print(prefix); pw.print("animating="); pw.print(animating);
-                    pw.print(" animation="); pw.println(animation);
-        }
-        if (hasTransformation) {
-            pw.print(prefix); pw.print("XForm: ");
-                    transformation.printShortString(pw);
-                    pw.println();
-        }
-        if (animLayerAdjustment != 0) {
-            pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
-        }
         if (startingData != null || removed || firstWindowDrawn) {
             pw.print(prefix); pw.print("startingData="); pw.print(startingData);
                     pw.print(" removed="); pw.print(removed);
@@ -515,15 +272,6 @@
                     pw.print(" startingDisplayed="); pw.print(startingDisplayed);
                     pw.print(" startingMoved"); pw.println(startingMoved);
         }
-        if (thumbnail != null) {
-            pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
-                    pw.print(" x="); pw.print(thumbnailX);
-                    pw.print(" y="); pw.print(thumbnailY);
-                    pw.print(" layer="); pw.println(thumbnailLayer);
-            pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
-            pw.print(prefix); pw.print("thumbnailTransformation=");
-                    pw.println(thumbnailTransformation.toShortString());
-        }
     }
 
     @Override
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 0051d98..405dd04 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -57,12 +57,17 @@
     }
 
     /**
-     * Show the dim surface.
+     * Set's the dim surface's layer and update dim parameters that will be used in
+     * {@link #updateSurface} after all windows are examined.
      */
-    void show(int dw, int dh) {
+    void updateParameters(final Resources res, final Parameters params, final long currentTime) {
+        final int dw = params.mDimWidth;
+        final int dh = params.mDimHeight;
+        final WindowStateAnimator winAnimator = params.mDimWinAnimator;
+        final float target = params.mDimTarget;
         if (!mDimShown) {
-            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
-                    dw + "x" + dh + ")");
+            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+                "  DIM " + mDimSurface + ": SHOW pos=(0,0) (" + dw + "x" + dh + ")");
             mDimShown = true;
             try {
                 mLastDimWidth = dw;
@@ -78,17 +83,9 @@
             mLastDimHeight = dh;
             mDimSurface.setSize(dw, dh);
         }
-    }
 
-    /**
-     * Set's the dim surface's layer and update dim parameters that will be used in
-     * {@link #updateSurface} after all windows are examined.
-     */
-    void updateParameters(Resources res, WindowState w, long currentTime) {
-        final WindowStateAnimator winAnimator = w.mWinAnimator;
         mDimSurface.setLayer(winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
 
-        final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
         if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM "
                 + mDimSurface + ": layer=" + (winAnimator.mAnimLayer-1) + " target=" + target);
         if (mDimTargetAlpha != target) {
@@ -189,4 +186,18 @@
         pw.print(" delta="); pw.print(mDimDeltaPerMs);
         pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
     }
+
+    static class Parameters {
+        final WindowStateAnimator mDimWinAnimator;
+        final int mDimWidth;
+        final int mDimHeight;
+        final float mDimTarget;
+        Parameters(final WindowStateAnimator dimWinAnimator, final int dimWidth,
+                final int dimHeight, final float dimTarget) {
+            mDimWinAnimator = dimWinAnimator;
+            mDimWidth = dimWidth;
+            mDimHeight = dimHeight;
+            mDimTarget = dimTarget;
+        }
+    }
 }
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 5a104b2..77f94d9 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -6,6 +6,9 @@
 
 import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED;
+
+import static com.android.server.wm.WindowManagerService.H.SET_DIM_PARAMETERS;
 
 import android.content.Context;
 import android.os.SystemClock;
@@ -21,7 +24,6 @@
 import java.io.PrintWriter;
 
 /**
- * @author cmautner@google.com (Craig Mautner)
  * Singleton class that carries out the animations and Surface operations in a separate task
  * on behalf of WindowManagerService.
  */
@@ -67,6 +69,9 @@
 
     int mBulkUpdateParams = 0;
 
+    DimAnimator mDimAnimator = null;
+    DimAnimator.Parameters mDimParams = null;
+
     WindowAnimator(final WindowManagerService service, final Context context,
             final WindowManagerPolicy policy) {
         mService = service;
@@ -91,7 +96,8 @@
             if (mService.mWallpaperTarget == target
                     || mService.mLowerWallpaperTarget == target
                     || mService.mUpperWallpaperTarget == target) {
-                for (int i=0; i<mService.mWindows.size(); i++) {
+                final int N = mService.mWindows.size();
+                for (int i = 0; i < N; i++) {
                     WindowState w = mService.mWindows.get(i);
                     if (w.mIsWallpaper) {
                         target = w;
@@ -116,32 +122,34 @@
         int i;
         final int NAT = mService.mAppTokens.size();
         for (i=0; i<NAT; i++) {
-            final AppWindowToken appToken = mService.mAppTokens.get(i);
-            final boolean wasAnimating = appToken.animation != null
-                    && appToken.animation != WindowManagerService.sDummyAnimation;
-            if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+            final AppWindowAnimator appAnimator = mService.mAppTokens.get(i).mAppAnimator;
+            final boolean wasAnimating = appAnimator.animation != null
+                    && appAnimator.animation != WindowManagerService.sDummyAnimation;
+            if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
                 mAnimating = true;
             } else if (wasAnimating) {
                 // stopped animating, do one more pass through the layout
                 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                 if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                    mService.debugLayoutRepeats("appToken " + appToken + " done");
+                    mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
+                        mPendingLayoutChanges);
                 }
             }
         }
 
         final int NEAT = mService.mExitingAppTokens.size();
         for (i=0; i<NEAT; i++) {
-            final AppWindowToken appToken = mService.mExitingAppTokens.get(i);
-            final boolean wasAnimating = appToken.animation != null
-                    && appToken.animation != WindowManagerService.sDummyAnimation;
-            if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+            final AppWindowAnimator appAnimator = mService.mAppTokens.get(i).mAppAnimator;
+            final boolean wasAnimating = appAnimator.animation != null
+                    && appAnimator.animation != WindowManagerService.sDummyAnimation;
+            if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
                 mAnimating = true;
             } else if (wasAnimating) {
                 // stopped animating, do one more pass through the layout
                 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                 if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                    mService.debugLayoutRepeats("exiting appToken " + appToken + " done");
+                    mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken 
+                        + " done", mPendingLayoutChanges);
                 }
             }
         }
@@ -185,13 +193,13 @@
                                 && winAnimator.mAnimation.getDetachWallpaper()) {
                             mDetachedWallpaper = w;
                         }
-                        if (winAnimator.mAnimation.getBackgroundColor() != 0) {
+                        final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
+                        if (backgroundColor != 0) {
                             if (mWindowAnimationBackground == null
                                     || (winAnimator.mAnimLayer <
                                             mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
                                 mWindowAnimationBackground = w;
-                                mWindowAnimationBackgroundColor =
-                                        winAnimator.mAnimation.getBackgroundColor();
+                                mWindowAnimationBackgroundColor = backgroundColor;
                             }
                         }
                     }
@@ -201,19 +209,21 @@
                 // If this window's app token is running a detached wallpaper
                 // animation, make a note so we can ensure the wallpaper is
                 // displayed behind it.
-                if (w.mAppToken != null && w.mAppToken.animation != null
-                        && w.mAppToken.animating) {
+                final AppWindowAnimator appAnimator =
+                        w.mAppToken == null ? null : w.mAppToken.mAppAnimator;
+                if (appAnimator != null && appAnimator.animation != null
+                        && appAnimator.animating) {
                     if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0
-                            && w.mAppToken.animation.getDetachWallpaper()) {
+                            && appAnimator.animation.getDetachWallpaper()) {
                         mDetachedWallpaper = w;
                     }
-                    if (w.mAppToken.animation.getBackgroundColor() != 0) {
+                    final int backgroundColor = appAnimator.animation.getBackgroundColor();
+                    if (backgroundColor != 0) {
                         if (mWindowAnimationBackground == null
                                 || (winAnimator.mAnimLayer <
                                         mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
                             mWindowAnimationBackground = w;
-                            mWindowAnimationBackgroundColor =
-                                    w.mAppToken.animation.getBackgroundColor();
+                            mWindowAnimationBackgroundColor = backgroundColor;
                         }
                     }
                 }
@@ -222,7 +232,8 @@
                     mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
                     mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                        mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2");
+                        mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
+                            mPendingLayoutChanges);
                     }
                 }
 
@@ -231,17 +242,18 @@
                         if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
                                 "Animation started that could impact force hide: "
                                 + w);
-                        mService.mInnerFields.mWallpaperForceHidingChanged = true;
+                        mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
                         mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                         if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                            mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3");
+                            mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
+                                mPendingLayoutChanges);
                         }
                         mService.mFocusMayChange = true;
                     } else if (w.isReadyForDisplay() && winAnimator.mAnimation == null) {
                         mForceHiding = true;
                     }
                 } else if (mPolicy.canBeForceHidden(w, attrs)) {
-                    boolean changed;
+                    final boolean changed;
                     if (mForceHiding) {
                         changed = w.hideLw(false, false);
                         if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
@@ -251,7 +263,7 @@
                         if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
                                 "Now policy shown: " + w);
                         if (changed) {
-                            if (mService.mInnerFields.mWallpaperForceHidingChanged
+                            if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
                                     && w.isVisibleNow() /*w.isReadyForDisplay()*/) {
                                 // Assume we will need to animate.  If
                                 // we don't (because the wallpaper will
@@ -275,14 +287,15 @@
                         mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
                         mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                         if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                            mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4");
+                            mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
+                                mPendingLayoutChanges);
                         }
                     }
                 }
             }
 
             final AppWindowToken atoken = w.mAppToken;
-            if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+            if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
                 if (atoken.lastTransactionSequence != mTransactionSequence) {
                     atoken.lastTransactionSequence = mTransactionSequence;
                     atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
@@ -306,14 +319,14 @@
                         }
                     }
                     if (w != atoken.startingWindow) {
-                        if (!atoken.freezingScreen || !w.mAppFreezing) {
+                        if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
                             atoken.numInterestingWindows++;
                             if (w.isDrawnLw()) {
                                 atoken.numDrawnWindows++;
                                 if (WindowManagerService.DEBUG_VISIBILITY ||
                                         WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
                                         "tokenMayBeDrawn: " + atoken
-                                        + " freezingScreen=" + atoken.freezingScreen
+                                        + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
                                         + " mAppFreezing=" + w.mAppFreezing);
                                 mTokenMayBeDrawn = true;
                             }
@@ -326,17 +339,20 @@
                 if (winAnimator.performShowLocked()) {
                     mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
                     if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                        mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5");
+                        mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
+                            mPendingLayoutChanges);
                     }
                 }
             }
-            if (atoken != null && atoken.thumbnail != null) {
-                if (atoken.thumbnailTransactionSeq != mTransactionSequence) {
-                    atoken.thumbnailTransactionSeq = mTransactionSequence;
-                    atoken.thumbnailLayer = 0;
+            final AppWindowAnimator appAnimator =
+                    atoken == null ? null : atoken.mAppAnimator;
+            if (appAnimator != null && appAnimator.thumbnail != null) {
+                if (appAnimator.thumbnailTransactionSeq != mTransactionSequence) {
+                    appAnimator.thumbnailTransactionSeq = mTransactionSequence;
+                    appAnimator.thumbnailLayer = 0;
                 }
-                if (atoken.thumbnailLayer < winAnimator.mAnimLayer) {
-                    atoken.thumbnailLayer = winAnimator.mAnimLayer;
+                if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
+                    appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
                 }
             }
         } // end forall windows
@@ -348,7 +364,7 @@
         final int NT = mService.mAppTokens.size();
         for (int i=0; i<NT; i++) {
             AppWindowToken wtoken = mService.mAppTokens.get(i);
-            if (wtoken.freezingScreen) {
+            if (wtoken.mAppAnimator.freezingScreen) {
                 int numInteresting = wtoken.numInterestingWindows;
                 if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
                     if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -361,7 +377,6 @@
                             "Setting mOrientationChangeComplete=true because wtoken "
                             + wtoken + " numInteresting=" + numInteresting
                             + " numDrawn=" + wtoken.numDrawnWindows);
-                    mService.mInnerFields.mOrientationChangeComplete = true;
                 }
             } else if (!wtoken.allDrawn) {
                 int numInteresting = wtoken.numInterestingWindows;
@@ -373,7 +388,8 @@
                     wtoken.allDrawn = true;
                     mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
                     if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                        mService.debugLayoutRepeats("testTokenMayBeDrawnLocked");
+                        mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
+                            mPendingLayoutChanges);
                     }
 
                     // We can now show all of the drawn windows!
@@ -419,14 +435,13 @@
                 mScreenRotationAnimation.updateSurfaces();
             }
 
-            final int N = mService.mWindows.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
                 WindowState w = mService.mWindows.get(i);
                 w.mWinAnimator.prepareSurfaceLocked(true);
             }
 
-            if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) {
-                mAnimating |= mService.mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
+            if (mDimAnimator != null && mDimAnimator.mDimShown) {
+                mAnimating |= mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
                             mCurrentTime, !mService.okToDisplay());
             }
 
@@ -438,6 +453,10 @@
                     mService.mBlackFrame.clearMatrix();
                 }
             }
+
+            if (mDimParams != null) {
+                mDimAnimator.updateParameters(mContext.getResources(), mDimParams, mCurrentTime);
+            }
         } catch (RuntimeException e) {
             Log.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
@@ -450,7 +469,7 @@
     }
 
     WindowState mCurrentFocus;
-    void setCurrentFocus(WindowState currentFocus) {
+    void setCurrentFocus(final WindowState currentFocus) {
         mCurrentFocus = currentFocus;
     }
 
@@ -462,6 +481,20 @@
         mInnerDh = appHeight;
     }
 
+    void startDimming(final WindowStateAnimator winAnimator, final float target,
+                      final int width, final int height) {
+        if (mDimAnimator == null) {
+            mDimAnimator = new DimAnimator(mService.mFxSession);
+        }
+        mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
+                new DimAnimator.Parameters(winAnimator, width, height, target)));
+    }
+
+    // TODO(cmautner): Move into Handler
+    void stopDimming() {
+        mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS, null));
+    }
+
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         if (mWindowDetachedWallpaper != null) {
             pw.print("  mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
@@ -470,5 +503,11 @@
             pw.println("  mWindowAnimationBackgroundSurface:");
             mWindowAnimationBackgroundSurface.printTo("    ", pw);
         }
+        if (mDimAnimator != null) {
+            pw.println("  mDimAnimator:");
+            mDimAnimator.printTo("    ", pw);
+        } else {
+            pw.println( "  no DimAnimator ");
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index afbc348..7eca401 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -149,7 +149,7 @@
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
     static final String TAG = "WindowManager";
     static final boolean DEBUG = false;
-    static final boolean DEBUG_ADD_REMOVE = true;
+    static final boolean DEBUG_ADD_REMOVE = false;
     static final boolean DEBUG_FOCUS = false;
     static final boolean DEBUG_ANIM = false;
     static final boolean DEBUG_LAYOUT = false;
@@ -158,7 +158,7 @@
     static final boolean DEBUG_INPUT = false;
     static final boolean DEBUG_INPUT_METHOD = false;
     static final boolean DEBUG_VISIBILITY = false;
-    static final boolean DEBUG_WINDOW_MOVEMENT = true;
+    static final boolean DEBUG_WINDOW_MOVEMENT = false;
     static final boolean DEBUG_TOKEN_MOVEMENT = false;
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_ORIENTATION = false;
@@ -171,7 +171,7 @@
     static final boolean DEBUG_SCREEN_ON = false;
     static final boolean DEBUG_SCREENSHOT = false;
     static final boolean DEBUG_BOOT = false;
-    static final boolean DEBUG_LAYOUT_REPEATS = false;
+    static final boolean DEBUG_LAYOUT_REPEATS = true;
     static final boolean SHOW_SURFACE_ALLOC = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -353,7 +353,32 @@
      * controlling the ordering of windows in different applications.  This
      * contains AppWindowToken objects.
      */
-    final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
+    final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>() {
+        @Override
+        public void add(int index, AppWindowToken object) {
+            synchronized (mAnimator) {
+                super.add(index, object);
+            }
+        };
+        @Override
+        public boolean add(AppWindowToken object) {
+            synchronized (mAnimator) {
+                return super.add(object);
+            }
+        };
+        @Override
+        public AppWindowToken remove(int index) {
+            synchronized (mAnimator) {
+                return super.remove(index);
+            }
+        };
+        @Override
+        public boolean remove(Object object) {
+            synchronized (mAnimator) {
+                return super.remove(object);
+            }
+        };
+    };
 
     /**
      * Application tokens that are in the process of exiting, but still
@@ -370,7 +395,32 @@
     /**
      * Z-ordered (bottom-most first) list of all Window objects.
      */
-    final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
+    final ArrayList<WindowState> mWindows = new ArrayList<WindowState>() {
+        @Override
+        public void add(int index, WindowState object) {
+            synchronized (mAnimator) {
+                super.add(index, object);
+            }
+        };
+        @Override
+        public boolean add(WindowState object) {
+            synchronized (mAnimator) {
+                return super.add(object);
+            }
+        };
+        @Override
+        public WindowState remove(int index) {
+            synchronized (mAnimator) {
+                return super.remove(index);
+            }
+        };
+        @Override
+        public boolean remove(Object object) {
+            synchronized (mAnimator) {
+                return super.remove(object);
+            }
+        };
+    };
 
     /**
      * Fake windows added to the window manager.  Note: ordered from top to
@@ -427,7 +477,6 @@
     IInputMethodManager mInputMethodManager;
 
     SurfaceSession mFxSession;
-    DimAnimator mDimAnimator = null;
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
 
@@ -590,8 +639,10 @@
     /** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple
      * methods. */
     class LayoutFields {
-        static final int SET_UPDATE_ROTATION        = 1 << 0;
-        static final int SET_WALLPAPER_MAY_CHANGE   = 1 << 1;
+        static final int SET_UPDATE_ROTATION                = 1 << 0;
+        static final int SET_WALLPAPER_MAY_CHANGE           = 1 << 1;
+        static final int SET_FORCE_HIDING_CHANGED           = 1 << 2;
+        static final int CLEAR_ORIENTATION_CHANGE_COMPLETE  = 1 << 3;
 
         boolean mWallpaperForceHidingChanged = false;
         boolean mWallpaperMayChange = false;
@@ -1214,7 +1265,7 @@
                 AppWindowToken token = curTarget.mAppToken;
                 WindowState highestTarget = null;
                 int highestPos = 0;
-                if (token.animating || token.animation != null) {
+                if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
                     int pos = localmWindows.indexOf(curTarget);
                     while (pos >= 0) {
                         WindowState win = localmWindows.get(pos);
@@ -1274,7 +1325,7 @@
                 mInputMethodTarget = w;
                 mInputMethodTargetWaitingAnim = false;
                 if (w.mAppToken != null) {
-                    setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
+                    setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment);
                 } else {
                     setInputMethodAnimLayerAdjustment(0);
                 }
@@ -1537,12 +1588,12 @@
         if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
                 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
                 + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
-                        ? wallpaperTarget.mAppToken.animation : null)
+                        ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
                 + " upper=" + mUpperWallpaperTarget
                 + " lower=" + mLowerWallpaperTarget);
         return (wallpaperTarget != null
                         && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
-                                && wallpaperTarget.mAppToken.animation != null)))
+                                && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
                 || mUpperWallpaperTarget != null
                 || mLowerWallpaperTarget != null;
     }
@@ -1582,7 +1633,7 @@
             if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
                 // If this window's app token is hidden and not animating,
                 // it is of no interest to us.
-                if (w.mAppToken.hidden && w.mAppToken.animation == null) {
+                if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Skipping not hidden or animating token: " + w);
                     continue;
@@ -1597,7 +1648,7 @@
                 foundW = w;
                 foundI = i;
                 if (w == mWallpaperTarget && ((w.mAppToken != null
-                        && w.mAppToken.animation != null)
+                        && w.mAppToken.mAppAnimator.animation != null)
                         || w.mWinAnimator.mAnimation != null)) {
                     // The current wallpaper target is animating, so we'll
                     // look behind it for another possible target and figure
@@ -1656,9 +1707,11 @@
             // animating, then we are in our super special mode!
             if (foundW != null && oldW != null) {
                 boolean oldAnim = oldW.mWinAnimator.mAnimation != null
-                        || (oldW.mAppToken != null && oldW.mAppToken.animation != null);
+                        || (oldW.mAppToken != null
+                            && oldW.mAppToken.mAppAnimator.animation != null);
                 boolean foundAnim = foundW.mWinAnimator.mAnimation != null
-                        || (foundW.mAppToken != null && foundW.mAppToken.animation != null);
+                        || (foundW.mAppToken != null &&
+                            foundW.mAppToken.mAppAnimator.animation != null);
                 if (DEBUG_WALLPAPER) {
                     Slog.v(TAG, "New animation: " + foundAnim
                             + " old animation: " + oldAnim);
@@ -1711,10 +1764,10 @@
             // Is it time to stop animating?
             boolean lowerAnimating = mLowerWallpaperTarget.mWinAnimator.mAnimation != null
                     || (mLowerWallpaperTarget.mAppToken != null
-                            && mLowerWallpaperTarget.mAppToken.animation != null);
+                            && mLowerWallpaperTarget.mAppToken.mAppAnimator.animation != null);
             boolean upperAnimating = mUpperWallpaperTarget.mWinAnimator.mAnimation != null
                     || (mUpperWallpaperTarget.mAppToken != null
-                            && mUpperWallpaperTarget.mAppToken.animation != null);
+                            && mUpperWallpaperTarget.mAppToken.mAppAnimator.animation != null);
             if (!lowerAnimating || !upperAnimating) {
                 if (DEBUG_WALLPAPER) {
                     Slog.v(TAG, "No longer animating wallpaper targets!");
@@ -1736,7 +1789,7 @@
             // between two wallpaper targets.
             mWallpaperAnimLayerAdjustment =
                     (mLowerWallpaperTarget == null && foundW.mAppToken != null)
-                    ? foundW.mAppToken.animLayerAdjustment : 0;
+                    ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0;
 
             final int maxLayer = mPolicy.getMaxWallpaperLayer()
                     * TYPE_LAYER_MULTIPLIER
@@ -2007,6 +2060,7 @@
                     winAnimator.computeShownFrameLocked();
                     // No need to lay out the windows - we can just set the wallpaper position
                     // directly.
+                    // TODO(cmautner): Don't move this from here, just lock the WindowAnimator.
                     if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left
                             || winAnimator.mSurfaceY != wallpaper.mShownFrame.top) {
                         Surface.openTransaction();
@@ -2306,7 +2360,7 @@
                 + " mExiting=" + win.mExiting
                 + " isAnimating=" + win.mWinAnimator.isAnimating()
                 + " app-animation="
-                + (win.mAppToken != null ? win.mAppToken.animation : null)
+                + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
                 + " inPendingTransaction="
                 + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
                 + " mDisplayFrozen=" + mDisplayFrozen);
@@ -3174,13 +3228,13 @@
                     }
                     Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
                 }
-                wtoken.setAnimation(a, initialized);
+                wtoken.mAppAnimator.setAnimation(a, initialized);
             }
         } else {
-            wtoken.clearAnimation();
+            wtoken.mAppAnimator.clearAnimation();
         }
 
-        return wtoken.animation != null;
+        return wtoken.mAppAnimator.animation != null;
     }
 
     // -------------------------------------------------------------
@@ -3325,7 +3379,7 @@
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        
+
         // Get the dispatching timeout here while we are not holding any locks so that it
         // can be cached by the AppWindowToken.  The timeout value is used later by the
         // input dispatcher in code that does hold locks.  If we did not cache the value
@@ -3828,14 +3882,16 @@
                             wtoken.clientHidden = ttoken.clientHidden;
                             wtoken.sendAppVisibilityToClients();
                         }
-                        if (ttoken.animation != null) {
-                            wtoken.animation = ttoken.animation;
-                            wtoken.animating = ttoken.animating;
-                            wtoken.animLayerAdjustment = ttoken.animLayerAdjustment;
-                            ttoken.animation = null;
-                            ttoken.animLayerAdjustment = 0;
-                            wtoken.updateLayers();
-                            ttoken.updateLayers();
+                        final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+                        final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+                        if (tAppAnimator.animation != null) {
+                            wAppAnimator.animation = tAppAnimator.animation;
+                            wAppAnimator.animating = tAppAnimator.animating;
+                            wAppAnimator.animLayerAdjustment = tAppAnimator.animLayerAdjustment;
+                            tAppAnimator.animation = null;
+                            tAppAnimator.animLayerAdjustment = 0;
+                            wAppAnimator.updateLayers();
+                            tAppAnimator.updateLayers();
                         }
 
                         updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -3860,18 +3916,20 @@
                         mH.sendMessageAtFrontOfQueue(m);
                         return;
                     }
-                    if (ttoken.thumbnail != null) {
+                    final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+                    final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+                    if (tAppAnimator.thumbnail != null) {
                         // The old token is animating with a thumbnail, transfer
                         // that to the new token.
-                        if (wtoken.thumbnail != null) {
-                            wtoken.thumbnail.destroy();
+                        if (wAppAnimator.thumbnail != null) {
+                            wAppAnimator.thumbnail.destroy();
                         }
-                        wtoken.thumbnail = ttoken.thumbnail;
-                        wtoken.thumbnailX = ttoken.thumbnailX;
-                        wtoken.thumbnailY = ttoken.thumbnailY;
-                        wtoken.thumbnailLayer = ttoken.thumbnailLayer;
-                        wtoken.thumbnailAnimation = ttoken.thumbnailAnimation;
-                        ttoken.thumbnail = null;
+                        wAppAnimator.thumbnail = tAppAnimator.thumbnail;
+                        wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
+                        wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
+                        wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
+                        wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
+                        tAppAnimator.thumbnail = null;
                     }
                 }
             }
@@ -3961,12 +4019,12 @@
             boolean runningAppAnimation = false;
 
             if (transit != WindowManagerPolicy.TRANSIT_UNSET) {
-                if (wtoken.animation == sDummyAnimation) {
-                    wtoken.animation = null;
+                if (wtoken.mAppAnimator.animation == sDummyAnimation) {
+                    wtoken.mAppAnimator.animation = null;
                 }
                 applyAnimationLocked(wtoken, lp, transit, visible);
                 changed = true;
-                if (wtoken.animation != null) {
+                if (wtoken.mAppAnimator.animation != null) {
                     delayed = runningAppAnimation = true;
                 }
             }
@@ -4029,7 +4087,7 @@
             }
         }
 
-        if (wtoken.animation != null) {
+        if (wtoken.mAppAnimator.animation != null) {
             delayed = true;
         }
 
@@ -4074,7 +4132,7 @@
 
                 if (DEBUG_APP_TRANSITIONS) Slog.v(
                         TAG, "Setting dummy animation on: " + wtoken);
-                wtoken.setDummyAnimation();
+                wtoken.mAppAnimator.setDummyAnimation();
                 mOpeningApps.remove(wtoken);
                 mClosingApps.remove(wtoken);
                 wtoken.waitingToShow = wtoken.waitingToHide = false;
@@ -4124,7 +4182,7 @@
 
     void unsetAppFreezingScreenLocked(AppWindowToken wtoken,
             boolean unfreezeSurfaceNow, boolean force) {
-        if (wtoken.freezingScreen) {
+        if (wtoken.mAppAnimator.freezingScreen) {
             if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + wtoken
                     + " force=" + force);
             final int N = wtoken.allAppWindows.size();
@@ -4142,7 +4200,7 @@
             }
             if (force || unfrozeWindows) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken);
-                wtoken.freezingScreen = false;
+                wtoken.mAppAnimator.freezingScreen = false;
                 mAppsFreezingScreen--;
             }
             if (unfreezeSurfaceNow) {
@@ -4165,11 +4223,11 @@
             }
             Slog.i(TAG, "Set freezing of " + wtoken.appToken
                     + ": hidden=" + wtoken.hidden + " freezing="
-                    + wtoken.freezingScreen, e);
+                    + wtoken.mAppAnimator.freezingScreen, e);
         }
         if (!wtoken.hiddenRequested) {
-            if (!wtoken.freezingScreen) {
-                wtoken.freezingScreen = true;
+            if (!wtoken.mAppAnimator.freezingScreen) {
+                wtoken.mAppAnimator.freezingScreen = true;
                 mAppsFreezingScreen++;
                 if (mAppsFreezingScreen == 1) {
                     startFreezingDisplayLocked(false);
@@ -4222,7 +4280,7 @@
             }
             final long origId = Binder.clearCallingIdentity();
             if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + token
-                    + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen);
+                    + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.mAppAnimator.freezingScreen);
             unsetAppFreezingScreenLocked(wtoken, true, force);
             Binder.restoreCallingIdentity(origId);
         }
@@ -4257,8 +4315,8 @@
                 }
                 if (DEBUG_APP_TRANSITIONS) Slog.v(
                         TAG, "Removing app " + wtoken + " delayed=" + delayed
-                        + " animation=" + wtoken.animation
-                        + " animating=" + wtoken.animating);
+                        + " animation=" + wtoken.mAppAnimator.animation
+                        + " animating=" + wtoken.mAppAnimator.animating);
                 if (delayed) {
                     // set the token aside because it has an active animation to be finished
                     if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -4268,9 +4326,8 @@
                     // Make sure there is no animation running on this token,
                     // so any windows associated with it will be removed as
                     // soon as their animations are complete
-                    wtoken.clearAnimation();
-                    wtoken.animation = null;
-                    wtoken.animating = false;
+                    wtoken.mAppAnimator.clearAnimation();
+                    wtoken.mAppAnimator.animating = false;
                 }
                 if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
                         "removeAppToken: " + wtoken);
@@ -6657,6 +6714,7 @@
         public static final int ANIMATOR_WHAT_OFFSET = 100000;
         public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
         public static final int SET_WALLPAPER_OFFSET = ANIMATOR_WHAT_OFFSET + 2;
+        public static final int SET_DIM_PARAMETERS = ANIMATOR_WHAT_OFFSET + 3;
 
         private Session mLastReportedHold;
 
@@ -6980,14 +7038,16 @@
 
                 case APP_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
-                        Slog.w(TAG, "App freeze timeout expired.");
-                        int i = mAppTokens.size();
-                        while (i > 0) {
-                            i--;
-                            AppWindowToken tok = mAppTokens.get(i);
-                            if (tok.freezingScreen) {
-                                Slog.w(TAG, "Force clearing freeze: " + tok);
-                                unsetAppFreezingScreenLocked(tok, true, true);
+                        synchronized (mAnimator) {
+                            Slog.w(TAG, "App freeze timeout expired.");
+                            int i = mAppTokens.size();
+                            while (i > 0) {
+                                i--;
+                                AppWindowToken tok = mAppTokens.get(i);
+                                if (tok.mAppAnimator.freezingScreen) {
+                                    Slog.w(TAG, "Force clearing freeze: " + tok);
+                                    unsetAppFreezingScreenLocked(tok, true, true);
+                                }
                             }
                         }
                     }
@@ -7069,6 +7129,7 @@
                 }
 
                 case BULK_UPDATE_PARAMETERS: {
+                    // Used to send multiple changes from the animation side to the layout side.
                     synchronized (mWindowMap) {
                         // TODO(cmautner): As the number of bits grows, use masks of bit groups to
                         //  eliminate unnecessary tests.
@@ -7078,6 +7139,12 @@
                         if ((msg.arg1 & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
                             mInnerFields.mWallpaperMayChange = true;
                         }
+                        if ((msg.arg1 & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
+                            mInnerFields.mWallpaperForceHidingChanged = true;
+                        }
+                        if ((msg.arg1 & LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE) != 0) {
+                            mInnerFields.mOrientationChangeComplete = false;
+                        }
 
                         requestTraversalLocked();
                     }
@@ -7087,21 +7154,34 @@
                 // Animation messages. Move to Window{State}Animator
                 case SET_TRANSPARENT_REGION: {
                     // TODO(cmautner): Remove sync.
-                    synchronized (mWindowMap) {
+                    synchronized (mAnimator) {
                         Pair<WindowStateAnimator, Region> pair =
                                 (Pair<WindowStateAnimator, Region>) msg.obj;
                         final WindowStateAnimator winAnimator = pair.first;
                         winAnimator.setTransparentRegionHint(pair.second);
                     }
+
+                    scheduleAnimationLocked();
                     break;
                 }
 
                 case SET_WALLPAPER_OFFSET: {
                     // TODO(cmautner): Remove sync.
-                    synchronized (mWindowMap) {
+                    synchronized (mAnimator) {
                         final WindowStateAnimator winAnimator = (WindowStateAnimator) msg.obj;
                         winAnimator.setWallpaperOffset(msg.arg1, msg.arg2);
                     }
+
+                    scheduleAnimationLocked();
+                    break;
+                }
+
+                case SET_DIM_PARAMETERS: {
+                    synchronized (mAnimator) {
+                        mAnimator.mDimParams = (DimAnimator.Parameters) msg.obj;
+                    }
+
+                    scheduleAnimationLocked();
                     break;
                 }
             }
@@ -7450,9 +7530,11 @@
                 w.mLayer = curLayer;
             }
             if (w.mTargetAppToken != null) {
-                w.mWinAnimator.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
+                w.mWinAnimator.mAnimLayer =
+                        w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
             } else if (w.mAppToken != null) {
-                w.mWinAnimator.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
+                w.mWinAnimator.mAnimLayer =
+                        w.mLayer + w.mAppToken.mAppAnimator.animLayerAdjustment;
             } else {
                 w.mWinAnimator.mAnimLayer = w.mLayer;
             }
@@ -7886,10 +7968,10 @@
                 AppWindowToken wtoken = mOpeningApps.get(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "Now opening app" + wtoken);
-                wtoken.clearThumbnail();
+                wtoken.mAppAnimator.clearThumbnail();
                 wtoken.reportedVisible = false;
                 wtoken.inPendingTransaction = false;
-                wtoken.animation = null;
+                wtoken.mAppAnimator.animation = null;
                 setTokenVisibilityLocked(wtoken, animLp, true,
                         transit, false);
                 wtoken.updateReportedVisibilityLocked();
@@ -7914,9 +7996,9 @@
                 AppWindowToken wtoken = mClosingApps.get(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "Now closing app" + wtoken);
-                wtoken.clearThumbnail();
+                wtoken.mAppAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
-                wtoken.animation = null;
+                wtoken.mAppAnimator.animation = null;
                 setTokenVisibilityLocked(wtoken, animLp, false,
                         transit, false);
                 wtoken.updateReportedVisibilityLocked();
@@ -7928,7 +8010,7 @@
             }
 
             if (mNextAppTransitionThumbnail != null && topOpeningApp != null
-                    && topOpeningApp.animation != null) {
+                    && topOpeningApp.mAppAnimator.animation != null) {
                 // This thumbnail animation is very special, we need to have
                 // an extra surface with the thumbnail included with the animation.
                 Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
@@ -7937,7 +8019,7 @@
                     Surface surface = new Surface(mFxSession, Process.myPid(),
                             "thumbnail anim", 0, dirty.width(), dirty.height(),
                             PixelFormat.TRANSLUCENT, Surface.HIDDEN);
-                    topOpeningApp.thumbnail = surface;
+                    topOpeningApp.mAppAnimator.thumbnail = surface;
                     if (SHOW_TRANSACTIONS) Slog.i(TAG, "  THUMBNAIL "
                             + surface + ": CREATE");
                     Surface drawSurface = new Surface();
@@ -7946,17 +8028,17 @@
                     c.drawBitmap(mNextAppTransitionThumbnail, 0, 0, null);
                     drawSurface.unlockCanvasAndPost(c);
                     drawSurface.release();
-                    topOpeningApp.thumbnailLayer = topOpeningLayer;
+                    topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer;
                     Animation anim = createThumbnailAnimationLocked(transit, true, true);
-                    topOpeningApp.thumbnailAnimation = anim;
+                    topOpeningApp.mAppAnimator.thumbnailAnimation = anim;
                     anim.restrictDuration(MAX_ANIMATION_DURATION);
                     anim.scaleCurrentDuration(mTransitionAnimationScale);
-                    topOpeningApp.thumbnailX = mNextAppTransitionStartX;
-                    topOpeningApp.thumbnailY = mNextAppTransitionStartY;
+                    topOpeningApp.mAppAnimator.thumbnailX = mNextAppTransitionStartX;
+                    topOpeningApp.mAppAnimator.thumbnailY = mNextAppTransitionStartY;
                 } catch (Surface.OutOfResourcesException e) {
                     Slog.e(TAG, "Can't allocate thumbnail surface w=" + dirty.width()
                             + " h=" + dirty.height(), e);
-                    topOpeningApp.clearThumbnail();
+                    topOpeningApp.mAppAnimator.clearThumbnail();
                 }
             }
 
@@ -8038,7 +8120,6 @@
             }
         }
         mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
-        mInnerFields.mWallpaperForceHidingChanged = false;
         if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
                 + " NEW: " + mWallpaperTarget
                 + " LOWER: " + mLowerWallpaperTarget);
@@ -8177,16 +8258,16 @@
             if (!mInnerFields.mDimming) {
                 //Slog.i(TAG, "DIM BEHIND: " + w);
                 mInnerFields.mDimming = true;
-                if (mDimAnimator == null) {
-                    mDimAnimator = new DimAnimator(mFxSession);
-                }
+                final int width, height;
                 if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
-                    mDimAnimator.show(mCurDisplayWidth, mCurDisplayHeight);
+                    width = mCurDisplayWidth;
+                    height = mCurDisplayHeight;
                 } else {
-                    mDimAnimator.show(innerDw, innerDh);
+                    width = innerDw;
+                    height = innerDh;
                 }
-                mDimAnimator.updateParameters(mContext.getResources(),
-                        w, currentTime);
+                mAnimator.startDimming(w.mWinAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount,
+                        width, height);
             }
         }
     }
@@ -8223,7 +8304,6 @@
             mExitingAppTokens.get(i).hasVisible = false;
         }
 
-        mInnerFields.mOrientationChangeComplete = true;
         mInnerFields.mHoldScreen = null;
         mInnerFields.mScreenBrightness = -1;
         mInnerFields.mButtonBrightness = -1;
@@ -8252,7 +8332,6 @@
         }
 
         try {
-            mInnerFields.mWallpaperForceHidingChanged = false;
             int repeats = 0;
             
             do {
@@ -8263,7 +8342,8 @@
                     break;
                 }
 
-                if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner");
+                if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+                    mPendingLayoutChanges);
 
                 if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
                     if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
@@ -8294,7 +8374,8 @@
                 // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
                 // it is animating.
                 mPendingLayoutChanges = 0;
-                if (DEBUG_LAYOUT_REPEATS)  debugLayoutRepeats("loop number " + mLayoutRepeatCount);
+                if (DEBUG_LAYOUT_REPEATS)  debugLayoutRepeats("loop number " + mLayoutRepeatCount,
+                    mPendingLayoutChanges);
                 mPolicy.beginAnimationLw(dw, dh);
                 for (i = mWindows.size() - 1; i >= 0; i--) {
                     WindowState w = mWindows.get(i);
@@ -8303,7 +8384,8 @@
                     }
                 }
                 mPendingLayoutChanges |= mPolicy.finishAnimationLw();
-                if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw");
+                if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw",
+                    mPendingLayoutChanges);
             } while (mPendingLayoutChanges != 0);
 
             final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
@@ -8337,6 +8419,9 @@
                     updateWallpaperVisibilityLocked();
                 }
             }
+            if (!mInnerFields.mDimming) {
+                mAnimator.stopDimming();
+            }
         } catch (RuntimeException e) {
             Log.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
@@ -8348,7 +8433,8 @@
         // to go.
         if (mAppTransitionReady) {
             mPendingLayoutChanges |= handleAppTransitionReadyLocked();
-            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked");
+            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
+                mPendingLayoutChanges);
         }
 
         mInnerFields.mAdjResult = 0;
@@ -8361,7 +8447,8 @@
             // be out of sync with it.  So here we will just rebuild the
             // entire app window list.  Fun!
             mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
-            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock");
+            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
+                mPendingLayoutChanges);
         }
 
         if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
@@ -8373,9 +8460,10 @@
             // hard -- the wallpaper now needs to be shown behind
             // something that was hidden.
             mPendingLayoutChanges |= animateAwayWallpaperLocked();
-            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked");
+            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
+                mPendingLayoutChanges);
         }
-        
+        mInnerFields.mWallpaperForceHidingChanged = false;
 
         if (mInnerFields.mWallpaperMayChange) {
             if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
@@ -8405,7 +8493,7 @@
 
         if (mLayoutNeeded) {
             mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded");
+            if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
         }
 
         final int N = mWindows.size();
@@ -8426,7 +8514,8 @@
                         mInnerFields.mWallpaperMayChange = true;
                         mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                         if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                            debugLayoutRepeats("updateWindowsAndWallpaperLocked 1");
+                            debugLayoutRepeats("updateWindowsAndWallpaperLocked 1",
+                                mPendingLayoutChanges);
                         }
                     }
                 }
@@ -8455,7 +8544,7 @@
         // associated with exiting/removed apps
         mAnimator.animate();
         mPendingLayoutChanges |= mAnimator.mPendingLayoutChanges;
-        if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()");
+        if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()", mPendingLayoutChanges);
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
@@ -8550,9 +8639,8 @@
                 // Make sure there is no animation running on this token,
                 // so any windows associated with it will be removed as
                 // soon as their animations are complete
-                token.clearAnimation();
-                token.animation = null;
-                token.animating = false;
+                token.mAppAnimator.clearAnimation();
+                token.mAppAnimator.animating = false;
                 if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
                         "performLayout: App token exiting now removed" + token);
                 mAppTokens.remove(token);
@@ -8629,6 +8717,7 @@
                 !mInnerFields.mUpdateRotation) {
             checkDrawnWindowsLocked();
         }
+        mInnerFields.mOrientationChangeComplete = true;
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
@@ -8822,7 +8911,7 @@
                 TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
             final WindowState oldFocus = mCurrentFocus;
             mCurrentFocus = newFocus;
-            mAnimator.setCurrentFocus(mCurrentFocus);
+            mAnimator.setCurrentFocus(newFocus);
             mLosingFocus.remove(newFocus);
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
 
@@ -8869,12 +8958,11 @@
         WindowState result = null;
         WindowState win;
 
-        int i = mWindows.size() - 1;
         int nextAppIndex = mAppTokens.size()-1;
         WindowToken nextApp = nextAppIndex >= 0
             ? mAppTokens.get(nextAppIndex) : null;
 
-        while (i >= 0) {
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
             win = mWindows.get(i);
 
             if (localLOGV || DEBUG_FOCUS) Slog.v(
@@ -8887,7 +8975,6 @@
 
             // If this window's application has been removed, just skip it.
             if (thisApp != null && thisApp.removed) {
-                i--;
                 continue;
             }
 
@@ -8927,8 +9014,6 @@
                 result = win;
                 break;
             }
-
-            i--;
         }
 
         return result;
@@ -9476,12 +9561,6 @@
             pw.print("  mSystemBooted="); pw.print(mSystemBooted);
                     pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
             pw.print("  mLayoutNeeded="); pw.println(mLayoutNeeded);
-            if (mDimAnimator != null) {
-                pw.println("  mDimAnimator:");
-                mDimAnimator.printTo("    ", pw);
-            } else {
-                pw.println( "  no DimAnimator ");
-            }
             pw.print("  mDisplayFrozen="); pw.print(mDisplayFrozen);
                     pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
                     pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
@@ -9691,10 +9770,10 @@
         requestTraversalLocked();
     }
 
-    void debugLayoutRepeats(final String msg) {
+    void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
         if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
-            Slog.v(TAG, "Layouts looping: " + msg);
-            Slog.v(TAG, "mPendingLayoutChanges = 0x" + Integer.toHexString(mPendingLayoutChanges));
+            Slog.v(TAG, "Layouts looping: " + msg + ", mPendingLayoutChanges = 0x" +
+                    Integer.toHexString(pendingLayoutChanges));
         }
     }
 
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index b74aa61..05797a4 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -65,6 +65,9 @@
     WindowToken mRootToken;
     AppWindowToken mAppToken;
     AppWindowToken mTargetAppToken;
+
+    // mAttrs.flags is tested in animation without being locked. If the bits tested are ever
+    // modified they will need to be locked.
     final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
     final DeathRecipient mDeathRecipient;
     final WindowState mAttachedWindow;
@@ -621,7 +624,7 @@
         }
         final AppWindowToken atoken = mAppToken;
         final boolean animating = atoken != null
-                ? (atoken.animation != null) : false;
+                ? (atoken.mAppAnimator.animation != null) : false;
         return mHasSurface && !mDestroying && !mExiting
                 && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
                 && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
@@ -637,7 +640,7 @@
     public boolean isWinVisibleLw() {
         final AppWindowToken atoken = mAppToken;
         return mHasSurface && mPolicyVisibility && !mAttachedHidden
-                && (atoken == null || !atoken.hiddenRequested || atoken.animating)
+                && (atoken == null || !atoken.hiddenRequested || atoken.mAppAnimator.animating)
                 && !mExiting && !mDestroying;
     }
 
@@ -685,7 +688,7 @@
         final AppWindowToken atoken = mAppToken;
         if (atoken != null) {
             return ((!mAttachedHidden && !atoken.hiddenRequested)
-                            || mWinAnimator.mAnimation != null || atoken.animation != null);
+                            || mWinAnimator.mAnimation != null || atoken.mAppAnimator.animation != null);
         }
         return !mAttachedHidden || mWinAnimator.mAnimation != null;
     }
@@ -703,7 +706,7 @@
                 && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
                                 && !mRootToken.hidden)
                         || mWinAnimator.mAnimation != null
-                        || ((mAppToken != null) && (mAppToken.animation != null)));
+                        || ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
     }
 
     /**
@@ -746,7 +749,7 @@
         return (mAttrs.format == PixelFormat.OPAQUE
                         || mAttrs.type == TYPE_WALLPAPER)
                 && isDrawnLw() && mWinAnimator.mAnimation == null
-                && (mAppToken == null || mAppToken.animation == null);
+                && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
     }
 
     /**
@@ -962,8 +965,9 @@
             pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
                     pw.print(" mSubLayer="); pw.print(mSubLayer);
                     pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
-                    pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
-                          : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
+                    pw.print((mTargetAppToken != null ?
+                            mTargetAppToken.mAppAnimator.animLayerAdjustment
+                          : (mAppToken != null ? mAppToken.mAppAnimator.animLayerAdjustment : 0)));
                     pw.print("="); pw.print(mWinAnimator.mAnimLayer);
                     pw.print(" mLastLayer="); pw.println(mWinAnimator.mLastLayer);
         }
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 4979a4c..6d0921e 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -5,6 +5,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static com.android.server.wm.WindowManagerService.LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE;
+
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -160,7 +162,7 @@
         return mAnimation != null
                 || (attached != null && attached.mWinAnimator.mAnimation != null)
                 || (atoken != null &&
-                        (atoken.animation != null
+                        (atoken.mAppAnimator.animation != null
                                 || atoken.inPendingTransaction));
     }
 
@@ -226,7 +228,7 @@
             }
             mHasLocalTransformation = false;
             if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null
-                    && mWin.mAppToken.animation != null) {
+                    && mWin.mAppToken.mAppAnimator.animation != null) {
                 // When our app token is animating, we kind-of pretend like
                 // we are as well.  Note the mLocalAnimating mAnimationIsEntrance
                 // part of this check means that we will only do this if
@@ -313,8 +315,9 @@
         }
 
         finishExit();
-        mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState");
+        mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
+                "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
 
         if (mWin.mAppToken != null) {
             mWin.mAppToken.updateReportedVisibilityLocked();
@@ -613,9 +616,10 @@
         Transformation attachedTransformation =
                 (mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
                 ? mAttachedWindow.mWinAnimator.mTransformation : null;
-        Transformation appTransformation =
-                (mWin.mAppToken != null && mWin.mAppToken.hasTransformation)
-                ? mWin.mAppToken.transformation : null;
+        final AppWindowAnimator appAnimator =
+                mWin.mAppToken == null ? null : mWin.mAppToken.mAppAnimator;
+        Transformation appTransformation = (appAnimator != null && appAnimator.hasTransformation)
+                ? appAnimator.transformation : null;
 
         // Wallpapers are animated based on the "real" window they
         // are currently targeting.
@@ -629,11 +633,13 @@
                     Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
                 }
             }
-            if (mService.mWallpaperTarget.mAppToken != null &&
-                    mService.mWallpaperTarget.mAppToken.hasTransformation &&
-                    mService.mWallpaperTarget.mAppToken.animation != null &&
-                    !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
-                appTransformation = mService.mWallpaperTarget.mAppToken.transformation;
+            final AppWindowAnimator wpAppAnimator = mService.mWallpaperTarget.mAppToken == null
+                    ? null : mService.mWallpaperTarget.mAppToken.mAppAnimator;
+            if (wpAppAnimator != null &&
+                    wpAppAnimator.hasTransformation &&
+                    wpAppAnimator.animation != null &&
+                    !wpAppAnimator.animation.getDetachWallpaper()) {
+                appTransformation = wpAppAnimator.transformation;
                 if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
                     Slog.v(TAG, "WP target app xform: " + appTransformation);
                 }
@@ -916,7 +922,7 @@
         if (displayed) {
             if (w.mOrientationChanging) {
                 if (!w.isDrawnLw()) {
-                    mService.mInnerFields.mOrientationChangeComplete = false;
+                    mAnimator.mBulkUpdateParams |= CLEAR_ORIENTATION_CHANGE_COMPLETE;
                     if (DEBUG_ORIENTATION) Slog.v(TAG,
                             "Orientation continue waiting for draw in " + w);
                 } else {
@@ -981,7 +987,7 @@
                     + (mWin.mAppToken != null ? mWin.mAppToken.hidden : false)
                     + " animating=" + mAnimating
                     + " tok animating="
-                    + (mWin.mAppToken != null ? mWin.mAppToken.animating : false));
+                    + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false));
             if (!showSurfaceRobustlyLocked()) {
                 return false;
             }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index ceda610..3775f9f 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -42,6 +42,15 @@
         </activity>
 
         <activity
+                android:name="DatePickerActivity"
+                android:label="_DatePicker">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="ClipRegionActivity"
                 android:label="_ClipRegion">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/res/layout/date_picker.xml b/tests/HwAccelerationTest/res/layout/date_picker.xml
new file mode 100644
index 0000000..742a03b
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/date_picker.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<!-- Layout of date picker-->
+
+<!-- The width of this container is manually set a little bigger than the one of the children
+     contained in it. This helps to prevent rounding errors when toggling the "Show year" option -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_gravity="center_horizontal"
+              android:layout_width="270dip"
+              android:layout_height="wrap_content">
+
+    <CheckBox
+            android:id="@+id/yearToggle"
+            android:text="Provide a year"
+            android:paddingTop="5dip"
+            android:paddingBottom="5dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    <!-- Warning: everything within the parent is removed and re-ordered depending
+         on the date format selected by the user. -->
+    <LinearLayout
+            android:id="@+id/parent"
+            android:orientation="horizontal"
+            android:layout_gravity="center_horizontal"
+            android:animateLayoutChanges="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+
+        <!-- Month -->
+        <NumberPicker
+                android:id="@+id/month"
+                android:layout_width="80dip"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="1dip"
+                android:layout_marginRight="1dip"
+                android:focusable="true"
+                android:focusableInTouchMode="true"
+                />
+
+        <!-- Day -->
+        <NumberPicker
+                android:id="@+id/day"
+                android:layout_width="80dip"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="1dip"
+                android:layout_marginRight="1dip"
+                android:focusable="true"
+                android:focusableInTouchMode="true"
+                />
+
+        <!-- Year -->
+        <NumberPicker
+                android:id="@+id/year"
+                android:layout_width="95dip"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="1dip"
+                android:layout_marginRight="1dip"
+                android:focusable="true"
+                android:focusableInTouchMode="true"
+                />
+    </LinearLayout>
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
new file mode 100644
index 0000000..db247e3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2012 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.test.hwui;
+
+import android.annotation.Widget;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * A view for selecting a month / year / day based on a calendar like layout.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
+ * For a dialog using this view, see {@link android.app.DatePickerDialog}.
+ */
+@Widget
+public class DatePicker extends FrameLayout {
+
+    private static final int DEFAULT_START_YEAR = 1900;
+    private static final int DEFAULT_END_YEAR = 2100;
+
+    /* UI Components */
+    private final CheckBox mYearToggle;
+    private final NumberPicker mDayPicker;
+    private final NumberPicker mMonthPicker;
+    private final NumberPicker mYearPicker;
+
+    /**
+     * How we notify users the date has changed.
+     */
+    private OnDateChangedListener mOnDateChangedListener;
+
+    private int mDay;
+    private int mMonth;
+    private int mYear;
+    private boolean mYearOptional = true;
+    private boolean mHasYear;
+
+    /**
+     * The callback used to indicate the user changes the date.
+     */
+    public interface OnDateChangedListener {
+
+        /**
+         * @param view The view associated with this listener.
+         * @param year The year that was set.
+         * @param monthOfYear The month that was set (0-11) for compatibility
+         *  with {@link java.util.Calendar}.
+         * @param dayOfMonth The day of the month that was set.
+         */
+        void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
+    }
+
+    public DatePicker(Context context) {
+        this(context, null);
+    }
+
+    public DatePicker(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    @SuppressWarnings("deprecation")
+    public DatePicker(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        ContextThemeWrapper themed = new ContextThemeWrapper(context,
+                com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert);
+        LayoutInflater inflater = (LayoutInflater) themed.getSystemService(
+                        Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.date_picker, this, true);
+
+        mDayPicker = (NumberPicker) findViewById(R.id.day);
+        mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+        mDayPicker.setOnLongPressUpdateInterval(100);
+        mDayPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                mDay = newVal;
+                notifyDateChanged();
+            }
+        });
+        mMonthPicker = (NumberPicker) findViewById(R.id.month);
+        mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+        DateFormatSymbols dfs = new DateFormatSymbols();
+        String[] months = dfs.getShortMonths();
+
+        /*
+         * If the user is in a locale where the month names are numeric,
+         * use just the number instead of the "month" character for
+         * consistency with the other fields.
+         */
+        if (months[0].startsWith("1")) {
+            for (int i = 0; i < months.length; i++) {
+                months[i] = String.valueOf(i + 1);
+            }
+            mMonthPicker.setMinValue(1);
+            mMonthPicker.setMaxValue(12);
+        } else {
+            mMonthPicker.setMinValue(1);
+            mMonthPicker.setMaxValue(12);
+            mMonthPicker.setDisplayedValues(months);
+        }
+
+        mMonthPicker.setOnLongPressUpdateInterval(200);
+        mMonthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+
+                /* We display the month 1-12 but store it 0-11 so always
+                 * subtract by one to ensure our internal state is always 0-11
+                 */
+                mMonth = newVal - 1;
+                // Adjust max day of the month
+                adjustMaxDay();
+                notifyDateChanged();
+                updateDaySpinner();
+            }
+        });
+        mYearPicker = (NumberPicker) findViewById(R.id.year);
+        mYearPicker.setOnLongPressUpdateInterval(100);
+        mYearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                mYear = newVal;
+                // Adjust max day for leap years if needed
+                adjustMaxDay();
+                notifyDateChanged();
+                updateDaySpinner();
+            }
+        });
+
+        mYearToggle = (CheckBox) findViewById(R.id.yearToggle);
+        mYearToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                mHasYear = isChecked;
+                adjustMaxDay();
+                notifyDateChanged();
+                updateSpinners();
+            }
+        });
+
+        // attributes
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.DatePicker);
+
+        int mStartYear =
+                a.getInt(com.android.internal.R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+        int mEndYear =
+                a.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+        mYearPicker.setMinValue(mStartYear);
+        mYearPicker.setMaxValue(mEndYear);
+
+        a.recycle();
+
+        // initialize to current date
+        Calendar cal = Calendar.getInstance();
+        init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), null);
+
+        // re-order the number pickers to match the current date format
+        reorderPickers(months);
+
+        if (!isEnabled()) {
+            setEnabled(false);
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        mDayPicker.setEnabled(enabled);
+        mMonthPicker.setEnabled(enabled);
+        mYearPicker.setEnabled(enabled);
+    }
+
+    private void reorderPickers(String[] months) {
+        java.text.DateFormat format;
+        String order;
+
+        /*
+         * If the user is in a locale where the medium date format is
+         * still numeric (Japanese and Czech, for example), respect
+         * the date format order setting.  Otherwise, use the order
+         * that the locale says is appropriate for a spelled-out date.
+         */
+
+        if (months[0].startsWith("1")) {
+            format = DateFormat.getDateFormat(getContext());
+        } else {
+            format = DateFormat.getMediumDateFormat(getContext());
+        }
+
+        if (format instanceof SimpleDateFormat) {
+            order = ((SimpleDateFormat) format).toPattern();
+        } else {
+            // Shouldn't happen, but just in case.
+            order = new String(DateFormat.getDateFormatOrder(getContext()));
+        }
+
+        /* Remove the 3 pickers from their parent and then add them back in the
+         * required order.
+         */
+        LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
+        parent.removeAllViews();
+
+        boolean quoted = false;
+        boolean didDay = false, didMonth = false, didYear = false;
+
+        for (int i = 0; i < order.length(); i++) {
+            char c = order.charAt(i);
+
+            if (c == '\'') {
+                quoted = !quoted;
+            }
+
+            if (!quoted) {
+                if (c == DateFormat.DATE && !didDay) {
+                    parent.addView(mDayPicker);
+                    didDay = true;
+                } else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) {
+                    parent.addView(mMonthPicker);
+                    didMonth = true;
+                } else if (c == DateFormat.YEAR && !didYear) {
+                    parent.addView (mYearPicker);
+                    didYear = true;
+                }
+            }
+        }
+
+        // Shouldn't happen, but just in case.
+        if (!didMonth) {
+            parent.addView(mMonthPicker);
+        }
+        if (!didDay) {
+            parent.addView(mDayPicker);
+        }
+        if (!didYear) {
+            parent.addView(mYearPicker);
+        }
+    }
+
+    public void updateDate(int year, int monthOfYear, int dayOfMonth) {
+        if (mYear != year || mMonth != monthOfYear || mDay != dayOfMonth) {
+            mYear = (mYearOptional && year == 0) ? getCurrentYear() : year;
+            mMonth = monthOfYear;
+            mDay = dayOfMonth;
+            updateSpinners();
+            reorderPickers(new DateFormatSymbols().getShortMonths());
+            notifyDateChanged();
+        }
+    }
+
+    private static int getCurrentYear() {
+        return Calendar.getInstance().get(Calendar.YEAR);
+    }
+
+    private static class SavedState extends BaseSavedState {
+
+        private final int mYear;
+        private final int mMonth;
+        private final int mDay;
+        private final boolean mHasYear;
+        private final boolean mYearOptional;
+
+        /**
+         * Constructor called from {@link DatePicker#onSaveInstanceState()}
+         */
+        private SavedState(Parcelable superState, int year, int month, int day, boolean hasYear,
+                boolean yearOptional) {
+            super(superState);
+            mYear = year;
+            mMonth = month;
+            mDay = day;
+            mHasYear = hasYear;
+            mYearOptional = yearOptional;
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            mYear = in.readInt();
+            mMonth = in.readInt();
+            mDay = in.readInt();
+            mHasYear = in.readInt() != 0;
+            mYearOptional = in.readInt() != 0;
+        }
+
+        public int getYear() {
+            return mYear;
+        }
+
+        public int getMonth() {
+            return mMonth;
+        }
+
+        public int getDay() {
+            return mDay;
+        }
+
+        public boolean hasYear() {
+            return mHasYear;
+        }
+
+        public boolean isYearOptional() {
+            return mYearOptional;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(mYear);
+            dest.writeInt(mMonth);
+            dest.writeInt(mDay);
+            dest.writeInt(mHasYear ? 1 : 0);
+            dest.writeInt(mYearOptional ? 1 : 0);
+        }
+
+        @SuppressWarnings("unused")
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Creator<SavedState>() {
+
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+
+
+    /**
+     * Override so we are in complete control of save / restore for this widget.
+     */
+    @Override
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        dispatchThawSelfOnly(container);
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+
+        return new SavedState(superState, mYear, mMonth, mDay, mHasYear, mYearOptional);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mYear = ss.getYear();
+        mMonth = ss.getMonth();
+        mDay = ss.getDay();
+        mHasYear = ss.hasYear();
+        mYearOptional = ss.isYearOptional();
+        updateSpinners();
+    }
+
+    /**
+     * Initialize the state.
+     * @param year The initial year.
+     * @param monthOfYear The initial month.
+     * @param dayOfMonth The initial day of the month.
+     * @param onDateChangedListener How user is notified date is changed by user, can be null.
+     */
+    public void init(int year, int monthOfYear, int dayOfMonth,
+            OnDateChangedListener onDateChangedListener) {
+        init(year, monthOfYear, dayOfMonth, false, onDateChangedListener);
+    }
+
+    /**
+     * Initialize the state.
+     * @param year The initial year or 0 if no year has been specified
+     * @param monthOfYear The initial month.
+     * @param dayOfMonth The initial day of the month.
+     * @param yearOptional True if the user can toggle the year
+     * @param onDateChangedListener How user is notified date is changed by user, can be null.
+     */
+    public void init(int year, int monthOfYear, int dayOfMonth, boolean yearOptional,
+            OnDateChangedListener onDateChangedListener) {
+        mYear = (yearOptional && year == 0) ? getCurrentYear() : year;
+        mMonth = monthOfYear;
+        mDay = dayOfMonth;
+        mYearOptional = yearOptional;
+        mHasYear = !yearOptional || (year != 0);
+        mOnDateChangedListener = onDateChangedListener;
+        updateSpinners();
+    }
+
+    private void updateSpinners() {
+        updateDaySpinner();
+        mYearToggle.setChecked(mHasYear);
+        mYearToggle.setVisibility(mYearOptional ? View.VISIBLE : View.GONE);
+        mYearPicker.setValue(mYear);
+        mYearPicker.setVisibility(mHasYear ? View.VISIBLE : View.GONE);
+
+        /* The month display uses 1-12 but our internal state stores it
+         * 0-11 so add one when setting the display.
+         */
+        mMonthPicker.setValue(mMonth + 1);
+    }
+
+    private void updateDaySpinner() {
+        Calendar cal = Calendar.getInstance();
+        // if year was not set, use 2000 as it was a leap year
+        cal.set(mHasYear ? mYear : 2000, mMonth, 1);
+        int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+        mDayPicker.setMinValue(1);
+        mDayPicker.setMaxValue(max);
+        mDayPicker.setValue(mDay);
+    }
+
+    public int getYear() {
+        return (mYearOptional && !mHasYear) ? 0 : mYear;
+    }
+
+    public int getMonth() {
+        return mMonth;
+    }
+
+    public int getDayOfMonth() {
+        return mDay;
+    }
+
+    private void adjustMaxDay(){
+        Calendar cal = Calendar.getInstance();
+        // if year was not set, use 2000 as it was a leap year
+        cal.set(Calendar.YEAR, mHasYear ? mYear : 2000);
+        cal.set(Calendar.MONTH, mMonth);
+        int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+        if (mDay > max) {
+            mDay = max;
+        }
+    }
+
+    private void notifyDateChanged() {
+        if (mOnDateChangedListener != null) {
+            int year = (mYearOptional && !mHasYear) ? 0 : mYear;
+            mOnDateChangedListener.onDateChanged(DatePicker.this, year, mMonth, mDay);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
new file mode 100644
index 0000000..5482ee2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class DatePickerActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        DatePicker picker = new DatePicker(this);
+        picker.init(2012, 3, 3, true, new DatePicker.OnDateChangedListener() {
+            @Override
+            public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+            }
+        });
+        setContentView(picker);
+        getWindow().setBackgroundDrawable(new ColorDrawable(0xffffffff));
+    }
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2fc6c20..c7f6bf0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -380,6 +380,7 @@
             mHandler = new P2pHandler(looper);
             mChannelListener = l;
         }
+        private final static int INVALID_LISTENER_KEY = 0;
         private ChannelListener mChannelListener;
         private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
         private Object mListenerMapLock = new Object();
@@ -450,16 +451,19 @@
         }
 
         int putListener(Object listener) {
-            if (listener == null) return 0;
+            if (listener == null) return INVALID_LISTENER_KEY;
             int key;
             synchronized (mListenerMapLock) {
-                key = mListenerKey++;
+                do {
+                    key = mListenerKey++;
+                } while (key == INVALID_LISTENER_KEY);
                 mListenerMap.put(key, listener);
             }
             return key;
         }
 
         Object getListener(int key) {
+            if (key == INVALID_LISTENER_KEY) return null;
             synchronized (mListenerMapLock) {
                 return mListenerMap.remove(key);
             }