Merge "AudioEffect JNI: use new max preprocessing constant" into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 979f2cf..a8c2ddc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16310,6 +16310,7 @@
     method public void setPlaybackToRemote(android.media.VolumeProvider);
     method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void setQueueTitle(java.lang.CharSequence);
+    method public void setRatingType(int);
     method public void setSessionActivity(android.app.PendingIntent);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
@@ -16368,6 +16369,7 @@
     method public long getBufferedPosition();
     method public java.util.List<android.media.session.PlaybackState.CustomAction> getCustomActions();
     method public java.lang.CharSequence getErrorMessage();
+    method public android.os.Bundle getExtras();
     method public long getLastPositionUpdateTime();
     method public float getPlaybackSpeed();
     method public long getPosition();
@@ -16412,6 +16414,7 @@
     method public android.media.session.PlaybackState.Builder setActiveQueueItemId(long);
     method public android.media.session.PlaybackState.Builder setBufferedPosition(long);
     method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.media.session.PlaybackState.Builder setExtras(android.os.Bundle);
     method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
     method public android.media.session.PlaybackState.Builder setState(int, long, float);
   }
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ba11a81..bc57030 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -417,7 +417,7 @@
             } else if (opt.equals("--ei")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
-                intent.putExtra(key, Integer.valueOf(value));
+                intent.putExtra(key, Integer.decode(value));
             } else if (opt.equals("--eu")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
@@ -434,7 +434,7 @@
                 String[] strings = value.split(",");
                 int[] list = new int[strings.length];
                 for (int i = 0; i < strings.length; i++) {
-                    list[i] = Integer.valueOf(strings[i]);
+                    list[i] = Integer.decode(strings[i]);
                 }
                 intent.putExtra(key, list);
             } else if (opt.equals("--el")) {
@@ -477,8 +477,23 @@
                 hasIntentInfo = true;
             } else if (opt.equals("--ez")) {
                 String key = nextArgRequired();
-                String value = nextArgRequired();
-                intent.putExtra(key, Boolean.valueOf(value));
+                String value = nextArgRequired().toLowerCase();
+                // Boolean.valueOf() results in false for anything that is not "true", which is
+                // error-prone in shell commands
+                boolean arg;
+                if ("true".equals(value) || "t".equals(value)) {
+                    arg = true;
+                } else if ("false".equals(value) || "f".equals(value)) {
+                    arg = false;
+                } else {
+                    try {
+                        arg = Integer.decode(value) != 0;
+                    } catch (NumberFormatException ex) {
+                        throw new IllegalArgumentException("Invalid boolean value: " + value);
+                    }
+                }
+
+                intent.putExtra(key, arg);
             } else if (opt.equals("-n")) {
                 String str = nextArgRequired();
                 ComponentName cn = ComponentName.unflattenFromString(str);
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 71df60a..6159e1e 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -200,7 +200,7 @@
         vendorInfo = info;
     }
 
-    public void setDomains(String domains) {
-        domains = domains;
+    public void setDomains(String newDomains) {
+        domains = newDomains;
     }
 }
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3f68a44..03a2085 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -41,10 +41,10 @@
  * <ul>
  * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
  * <li>Includes a receiver for {@link #ACTION_SCORE_NETWORKS} guarded by the
- *     {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission which scores networks
- *     and (eventually) calls {@link #updateScores} with the results. If this receiver specifies an
- *     android:label attribute, this label will be used when referring to the application throughout
- *     system settings; otherwise, the application label will be used.
+ *     {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission which scores
+ *     networks and (eventually) calls {@link #updateScores} with the results. If this receiver
+ *     specifies an android:label attribute, this label will be used when referring to the
+ *     application throughout system settings; otherwise, the application label will be used.
  * </ul>
  *
  * <p>The system keeps track of an active scorer application; at any time, only this application
@@ -194,8 +194,8 @@
      *
      * @return true if the operation succeeded, or false if the new package is not a valid scorer.
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating
-     *         that it can manage scorer applications.
+     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission
+     *         indicating that it can manage scorer applications.
      * @hide
      */
     public boolean setActiveScorer(String packageName) throws SecurityException {
@@ -228,7 +228,7 @@
      *
      * @return true if the broadcast was sent, or false if there is no active scorer.
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
      * @hide
      */
     public boolean requestScores(NetworkKey[] networks) throws SecurityException {
@@ -252,7 +252,7 @@
      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
      * @throws SecurityException if the caller does not hold the
-     *         {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+     *         {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
      * @throws IllegalArgumentException if a score cache is already registered for this type.
      * @hide
      */
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index c33f5ec..46f7194 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -79,7 +79,7 @@
      * <ul>
      * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
      * <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the
-     *     {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+     *     {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
      * </ul>
      *
      * @return the list of scorers, or the empty list if there are no valid scorers.
@@ -98,8 +98,8 @@
                 // Should never happen with queryBroadcastReceivers, but invalid nonetheless.
                 continue;
             }
-            if (!permission.BROADCAST_SCORE_NETWORKS.equals(receiverInfo.permission)) {
-                // Receiver doesn't require the BROADCAST_SCORE_NETWORKS permission, which means
+            if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) {
+                // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which means
                 // anyone could trigger network scoring and flood the framework with score requests.
                 continue;
             }
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 5a273cf..598a503 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -107,6 +107,7 @@
         for (InetAddress dns : dnsServers) {
             lp.addDnsServer(dns);
         }
+        lp.setDomains(domains);
         return lp;
     }
 
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 9fb3535..36401eb 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -78,6 +78,7 @@
     private static final String ALLOW_ATT_EVENTS = "events";
     private static final String SLEEP_TAG = "sleep";
     private static final String SLEEP_ATT_MODE = "mode";
+    private static final String SLEEP_ATT_NONE = "none";
 
     private static final String SLEEP_ATT_START_HR = "startHour";
     private static final String SLEEP_ATT_START_MIN = "startMin";
@@ -107,6 +108,7 @@
     public int sleepStartMinute; // 0-59
     public int sleepEndHour;
     public int sleepEndMinute;
+    public boolean sleepNone;    // false = priority, true = none
     public ComponentName[] conditionComponents;
     public Uri[] conditionIds;
     public Condition exitCondition;
@@ -125,6 +127,7 @@
         sleepStartMinute = source.readInt();
         sleepEndHour = source.readInt();
         sleepEndMinute = source.readInt();
+        sleepNone = source.readInt() == 1;
         int len = source.readInt();
         if (len > 0) {
             conditionComponents = new ComponentName[len];
@@ -155,6 +158,7 @@
         dest.writeInt(sleepStartMinute);
         dest.writeInt(sleepEndHour);
         dest.writeInt(sleepEndMinute);
+        dest.writeInt(sleepNone ? 1 : 0);
         if (conditionComponents != null && conditionComponents.length > 0) {
             dest.writeInt(conditionComponents.length);
             dest.writeTypedArray(conditionComponents, 0);
@@ -182,6 +186,7 @@
             .append(",sleepMode=").append(sleepMode)
             .append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
             .append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
+            .append(",sleepNone=").append(sleepNone)
             .append(",conditionComponents=")
             .append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents))
             .append(",conditionIds=")
@@ -214,6 +219,7 @@
                 && other.allowFrom == allowFrom
                 && other.allowEvents == allowEvents
                 && Objects.equals(other.sleepMode, sleepMode)
+                && other.sleepNone == sleepNone
                 && other.sleepStartHour == sleepStartHour
                 && other.sleepStartMinute == sleepStartMinute
                 && other.sleepEndHour == sleepEndHour
@@ -226,7 +232,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(allowCalls, allowMessages, allowFrom, allowEvents, sleepMode,
+        return Objects.hash(allowCalls, allowMessages, allowFrom, allowEvents, sleepMode, sleepNone,
                 sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
                 Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds),
                 exitCondition, exitConditionComponent);
@@ -302,6 +308,7 @@
                 } else if (SLEEP_TAG.equals(tag)) {
                     final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
                     rt.sleepMode = isValidSleepMode(mode)? mode : null;
+                    rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false);
                     final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
                     final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
                     final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
@@ -345,6 +352,7 @@
         if (sleepMode != null) {
             out.attribute(null, SLEEP_ATT_MODE, sleepMode);
         }
+        out.attribute(null, SLEEP_ATT_NONE, Boolean.toString(sleepNone));
         out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour));
         out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute));
         out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6f2a06b..ffe2b60 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2511,11 +2511,12 @@
         android:description="@string/permdesc_broadcastWapPush"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows an application to broadcast a SCORE_NETWORKS request.
+    <!-- @SystemApi Allows an application to broadcast privileged networking requests.
          <p>Not for use by third-party applications. @hide -->
-    <permission android:name="android.permission.BROADCAST_SCORE_NETWORKS"
-        android:label="@string/permlab_broadcastScoreNetworks"
-        android:description="@string/permdesc_broadcastScoreNetworks"
+    <permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
+        android:permissionGroup="android.permission-group.NETWORK"
+        android:label="@string/permlab_broadcastNetworkPrivileged"
+        android:description="@string/permdesc_broadcastNetworkPrivileged"
         android:protectionLevel="signature|system" />
 
     <!-- @SystemApi Not for use by third-party applications. -->
diff --git a/core/res/res/drawable-hdpi/sym_def_app_icon.png b/core/res/res/drawable-hdpi/sym_def_app_icon.png
index 96a442e..cde69bc 100644
--- a/core/res/res/drawable-hdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-hdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png
index 359047d..c133a0c 100644
--- a/core/res/res/drawable-mdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/sym_def_app_icon.png b/core/res/res/drawable-xhdpi/sym_def_app_icon.png
index 71c6d76..bfa42f0 100644
--- a/core/res/res/drawable-xhdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-xhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/sym_def_app_icon.png b/core/res/res/drawable-xxhdpi/sym_def_app_icon.png
index 20a47a0..324e72c 100644
--- a/core/res/res/drawable-xxhdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-xxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/sym_def_app_icon.png b/core/res/res/drawable-xxxhdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-hdpi/sym_def_app_icon.png b/core/res/res/mipmap-hdpi/sym_def_app_icon.png
index c8a38ed..cde69bc 100644
--- a/core/res/res/mipmap-hdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-hdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-mdpi/sym_def_app_icon.png b/core/res/res/mipmap-mdpi/sym_def_app_icon.png
index b3e10f6..c133a0c 100644
--- a/core/res/res/mipmap-mdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-xhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xhdpi/sym_def_app_icon.png
index f381f86..bfa42f0 100644
--- a/core/res/res/mipmap-xhdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-xhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
index e3f3144..324e72c 100644
--- a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-xxxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxxhdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/core/res/res/mipmap-xxxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index bb9885c..f6a5787 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -441,4 +441,13 @@
         <item>中文 (繁體)</item>
     </string-array>
 
+    <array name="sim_colors">
+        <item>@color/Teal_700</item>
+        <item>@color/Blue_700</item>
+        <item>@color/Indigo_700</item>
+        <item>@color/Purple_700</item>
+        <item>@color/Pink_700</item>
+        <item>@color/Red_700</item>
+    </array>
+
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 91a8598..67ba27da 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6388,7 +6388,7 @@
         <!-- The summary for the Preference in a PreferenceActivity screen. -->
         <attr name="summary" />
         <!-- The order for the Preference (lower values are to be ordered first). If this is not
-             specified, the default orderin will be alphabetic. -->
+             specified, the default ordering will be alphabetic. -->
         <attr name="order" format="integer" />
         <!-- When used inside of a modern PreferenceActivity, this declares
              a new PreferenceFragment to be shown when the user selects this item. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 9f83db4..b9825c5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -160,5 +160,18 @@
     <color name="user_icon_8">#ffff5722</color><!-- deep orange 500 -->
     <color name="user_icon_default_gray">#ff9e9e9e</color><!-- gray 500 -->
     <color name="user_icon_default_white">#ffffffff</color><!-- white -->
-</resources>
 
+    <!-- Multi-sim sim colors -->
+    <color name="Teal_700">#ff00796b</color>
+    <color name="Teal_800">#ff00695c</color>
+    <color name="Blue_700">#ff3367d6</color>
+    <color name="Blue_800">#ff2a56c6</color>
+    <color name="Indigo_700">#ff303f9f</color>
+    <color name="Indigo_800">#ff283593</color>
+    <color name="Purple_700">#ff7b1fa2</color>
+    <color name="Purple_800">#ff6a1b9a</color>
+    <color name="Pink_700">#ffc2185b</color>
+    <color name="Pink_800">#ffad1457</color>
+    <color name="Red_700">#ffc53929</color>
+    <color name="Red_800">#ffb93221</color>
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 652e40c..52ef4f9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -473,6 +473,8 @@
     <bool name="config_allowTheaterModeWakeFromDock">false</bool>
     <!-- If this is true, allow wake from theater mode from window layout flag. -->
     <bool name="config_allowTheaterModeWakeFromWindowLayout">false</bool>
+    <!-- If this is true, go to sleep when theater mode is enabled from button press -->
+    <bool name="config_goToSleepOnButtonPressTheaterMode">true</bool>
 
     <!-- Auto-rotation behavior -->
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3051234..c5bd495 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -977,10 +977,10 @@
 
     <!-- TODO: Mark these as translatable when API is finalized. -->
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_broadcastScoreNetworks" translatable="false">send score networks broadcast</string>
+    <string name="permlab_broadcastNetworkPrivileged" translatable="false">send privileged network broadcasts</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_broadcastScoreNetworks" translatable="false">Allows the app
-        to broadcast a notification that networks need to be scored.
+    <string name="permdesc_broadcastNetworkPrivileged" translatable="false">Allows the app
+        to send privileged network broadcasts.
         Never needed for normal apps.
     </string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a5f0ff1..6e881a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1073,6 +1073,7 @@
   <java-symbol type="array" name="networkAttributes" />
   <java-symbol type="array" name="preloaded_color_state_lists" />
   <java-symbol type="array" name="preloaded_drawables" />
+  <java-symbol type="array" name="sim_colors" />
   <java-symbol type="array" name="special_locale_codes" />
   <java-symbol type="array" name="special_locale_names" />
   <java-symbol type="array" name="config_masterVolumeRamp" />
@@ -1584,6 +1585,7 @@
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromLidSwitch" />
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" />
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
+  <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
   <java-symbol type="bool" name="config_wifi_background_scan_support" />
   <java-symbol type="bool" name="config_wifi_dual_band_support" />
   <java-symbol type="bool" name="config_wimaxEnabled" />
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index f916711..9bb44d0 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -59,7 +59,7 @@
         // Package 1 - Valid scorer.
         Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", true, true, false);
 
-        // Package 2 - Receiver does not have BROADCAST_SCORE_NETWORKS permission.
+        // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission.
         Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", false, true, false);
 
         // Package 3 - App does not have SCORE_NETWORKS permission.
@@ -134,7 +134,7 @@
         resolveInfo.activityInfo.packageName = packageName;
         resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
         if (hasReceiverPermission) {
-            resolveInfo.activityInfo.permission = permission.BROADCAST_SCORE_NETWORKS;
+            resolveInfo.activityInfo.permission = permission.BROADCAST_NETWORK_PRIVILEGED;
         }
 
         ResolveInfo configActivityInfo = null;
diff --git a/docs/html/images/tools/as-attach.png b/docs/html/images/tools/as-attach.png
new file mode 100644
index 0000000..c572b1e
--- /dev/null
+++ b/docs/html/images/tools/as-attach.png
Binary files differ
diff --git a/docs/html/sdk/installing/studio-debug.jd b/docs/html/sdk/installing/studio-debug.jd
index 2e3e137..b048400 100644
--- a/docs/html/sdk/installing/studio-debug.jd
+++ b/docs/html/sdk/installing/studio-debug.jd
@@ -6,7 +6,11 @@
 <div id="qv">
 <h2>In this document</h2>
 <ol>
-  <li><a href="#runDebug">Run your App in Debug Mode</a></li>
+  <li><a href="#runDebug">Run your App in Debug Mode</a>
+    <ol>
+      <li><a href="#attachDebug">Attach the debugger to a running process</a></li>
+    </ol>
+  </li>
   <li><a href="#systemLog">Use the System Log</a>
     <ol>
       <li><a href="#systemLogWrite">Write log messages in your code</a></li>
@@ -94,6 +98,22 @@
 <p class="img-caption"><strong>Figure 2.</strong> The Debug tool window in Android Studio showing
 the current thread and the object tree for a variable.</p>
 
+<h3 id="attachDebug">Attach the debugger to a running process</h3>
+
+<p>You don't always have to restart your app to debug it. To debug an app that you're already
+running:</p>
+
+<ol>
+<li>Click <strong>Attach debugger to Android proccess</strong>
+<img src="{@docRoot}images/tools/as-attach.png" alt=""
+style="vertical-align:bottom;margin:0;height:20px"/>.</li>
+<li>In the <em>Choose Process</em> window, select the device and app you want to attach the
+debugger to.</li>
+<li>To open the <em>Debug</em> tool window, click <strong>Debug</strong>
+<img src="{@docRoot}images/tools/as-debugwindowbutton.png"
+alt="" style="vertical-align:bottom;margin:0;height:20px"/>.</li>
+</ol>
+
 
 <h2 id="systemLog">Use the System Log</h2>
 
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index 6991dea..f02cdbc 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -340,7 +340,6 @@
 </td>
 </tr>
 </table>
-<p class="note"><strong>Note:</strong> The full SDK/NDK requires 13 GB of disk space.</p>
 </div><!-- end pax -->
 
 
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f1e4858..3cf1021 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3645,8 +3645,12 @@
     Entry entry;
     status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
     if (err != NO_ERROR) {
+        // Only log the failure when we're not running on the host as
+        // part of a tool. The caller will do its own logging.
+#ifndef STATIC_ANDROIDFW_FOR_TOOLS
         ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
                 resID, t, e, err);
+#endif
         return err;
     }
 
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 86da80a..973527f 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,9 @@
         if (volumeProvider == null) {
             throw new IllegalArgumentException("volumeProvider may not be null!");
         }
-        mVolumeProvider = volumeProvider;
+        synchronized (mLock) {
+            mVolumeProvider = volumeProvider;
+        }
         volumeProvider.setCallback(new VolumeProvider.Callback() {
             @Override
             public void onVolumeChanged(VolumeProvider volumeProvider) {
@@ -449,6 +451,27 @@
     }
 
     /**
+     * Set the style of rating used by this session. Apps trying to set the
+     * rating should use this style. Must be one of the following:
+     * <ul>
+     * <li>{@link Rating#RATING_NONE}</li>
+     * <li>{@link Rating#RATING_3_STARS}</li>
+     * <li>{@link Rating#RATING_4_STARS}</li>
+     * <li>{@link Rating#RATING_5_STARS}</li>
+     * <li>{@link Rating#RATING_HEART}</li>
+     * <li>{@link Rating#RATING_PERCENTAGE}</li>
+     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+     * </ul>
+     */
+    public void setRatingType(int type) {
+        try {
+            mBinder.setRatingType(type);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setRatingType.", e);
+        }
+    }
+
+    /**
      * Set some extras that can be associated with the {@link MediaSession}. No assumptions should
      * be made as to how a {@link MediaController} will handle these extras.
      * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
@@ -470,9 +493,11 @@
      * @hide
      */
     public void notifyRemoteVolumeChanged(VolumeProvider provider) {
-        if (provider == null || provider != mVolumeProvider) {
-            Log.w(TAG, "Received update from stale volume provider");
-            return;
+        synchronized (mLock) {
+            if (provider == null || provider != mVolumeProvider) {
+                Log.w(TAG, "Received update from stale volume provider");
+                return;
+            }
         }
         try {
             mBinder.setCurrentVolume(provider.getCurrentVolume());
@@ -537,6 +562,14 @@
         postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
     }
 
+    private void dispatchAdjustVolume(int direction) {
+        postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction);
+    }
+
+    private void dispatchSetVolumeTo(int volume) {
+        postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume);
+    }
+
     private void postToCallback(int what) {
         postToCallback(what, null);
     }
@@ -988,9 +1021,7 @@
         public void onAdjustVolume(int direction) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                if (session.mVolumeProvider != null) {
-                    session.mVolumeProvider.onAdjustVolume(direction);
-                }
+                session.dispatchAdjustVolume(direction);
             }
         }
 
@@ -998,9 +1029,7 @@
         public void onSetVolumeTo(int value) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                if (session.mVolumeProvider != null) {
-                    session.mVolumeProvider.onSetVolumeTo(value);
-                }
+                session.dispatchSetVolumeTo(value);
             }
         }
 
@@ -1117,6 +1146,8 @@
         private static final int MSG_CUSTOM_ACTION = 13;
         private static final int MSG_MEDIA_BUTTON = 14;
         private static final int MSG_COMMAND = 15;
+        private static final int MSG_ADJUST_VOLUME = 16;
+        private static final int MSG_SET_VOLUME = 17;
 
         private MediaSession.Callback mCallback;
 
@@ -1145,6 +1176,7 @@
 
         @Override
         public void handleMessage(Message msg) {
+            VolumeProvider vp;
             switch (msg.what) {
                 case MSG_PLAY:
                     mCallback.onPlay();
@@ -1192,6 +1224,22 @@
                     Command cmd = (Command) msg.obj;
                     mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
                     break;
+                case MSG_ADJUST_VOLUME:
+                    synchronized (mLock) {
+                        vp = mVolumeProvider;
+                    }
+                    if (vp != null) {
+                        vp.onAdjustVolume((int) msg.obj);
+                    }
+                    break;
+                case MSG_SET_VOLUME:
+                    synchronized (mLock) {
+                        vp = mVolumeProvider;
+                    }
+                    if (vp != null) {
+                        vp.onSetVolumeTo((int) msg.obj);
+                    }
+                    break;
             }
         }
     }
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 267d1ff..54d0acd 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -16,6 +16,7 @@
 package android.media.session;
 
 import android.annotation.DrawableRes;
+import android.annotation.Nullable;
 import android.media.RemoteControlClient;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -232,11 +233,12 @@
     private final CharSequence mErrorMessage;
     private final long mUpdateTime;
     private final long mActiveItemId;
+    private final Bundle mExtras;
 
     private PlaybackState(int state, long position, long updateTime, float speed,
             long bufferedPosition, long transportControls,
             List<PlaybackState.CustomAction> customActions, long activeItemId,
-            CharSequence error) {
+            CharSequence error, Bundle extras) {
         mState = state;
         mPosition = position;
         mSpeed = speed;
@@ -246,6 +248,7 @@
         mCustomActions = new ArrayList<>(customActions);
         mActiveItemId = activeItemId;
         mErrorMessage = error;
+        mExtras = extras;
     }
 
     private PlaybackState(Parcel in) {
@@ -258,7 +261,7 @@
         mCustomActions = in.createTypedArrayList(CustomAction.CREATOR);
         mActiveItemId = in.readLong();
         mErrorMessage = in.readCharSequence();
-
+        mExtras = in.readBundle();
     }
 
     @Override
@@ -293,6 +296,7 @@
         dest.writeTypedList(mCustomActions);
         dest.writeLong(mActiveItemId);
         dest.writeCharSequence(mErrorMessage);
+        dest.writeBundle(mExtras);
     }
 
     /**
@@ -306,6 +310,7 @@
      * <li> {@link PlaybackState#STATE_REWINDING}</li>
      * <li> {@link PlaybackState#STATE_BUFFERING}</li>
      * <li> {@link PlaybackState#STATE_ERROR}</li>
+     * </ul>
      */
     public int getState() {
         return mState;
@@ -394,6 +399,15 @@
     }
 
     /**
+     * Get any custom extras that were set on this playback state.
+     *
+     * @return The extras for this state or null.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
      * Get the {@link PlaybackState} state for the given
      * {@link RemoteControlClient} state.
      *
@@ -737,6 +751,7 @@
         private CharSequence mErrorMessage;
         private long mUpdateTime;
         private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
+        private Bundle mExtras;
 
         /**
          * Creates an initially empty state builder.
@@ -765,6 +780,7 @@
             mErrorMessage = from.mErrorMessage;
             mUpdateTime = from.mUpdateTime;
             mActiveItemId = from.mActiveItemId;
+            mExtras = from.mExtras;
         }
 
         /**
@@ -947,13 +963,25 @@
         }
 
         /**
-         * Build and return the {@link PlaybackState} instance with these values.
+         * Set any custom extras to be included with the playback state.
+         *
+         * @param extras The extras to include.
+         * @return this
+         */
+        public Builder setExtras(Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Build and return the {@link PlaybackState} instance with these
+         * values.
          *
          * @return A new state instance.
          */
         public PlaybackState build() {
             return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition,
-                    mActions, mCustomActions, mActiveItemId, mErrorMessage);
+                    mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras);
         }
     }
 }
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d50be42..0754fd4 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -101,7 +101,6 @@
      * be thrown.
      *
      * @see MediaBrowserService#onLoadChildren
-     * @see MediaBrowserService#onLoadIcon
      */
     public class Result<T> {
         private Object mDebug;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 792712f..3c44e87 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -48,6 +48,7 @@
 import android.media.RingtoneManager;
 import android.media.session.MediaSessionLegacyHelper;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.FactoryTest;
 import android.os.Handler;
 import android.os.IBinder;
@@ -116,6 +117,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.HashSet;
+import java.util.List;
 
 import static android.view.WindowManager.LayoutParams.*;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
@@ -527,6 +529,9 @@
     private boolean mAllowTheaterModeWakeFromLidSwitch;
     private boolean mAllowTheaterModeWakeFromWakeGesture;
 
+    // Whether to go to sleep entering theater mode from power button
+    private boolean mGoToSleepOnButtonPressTheaterMode;
+
     // Screenshot trigger states
     // Time to volume and power must be pressed within this interval of each other.
     private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
@@ -984,7 +989,8 @@
                     Slog.i(TAG, "Toggling theater mode on.");
                     Settings.Global.putInt(mContext.getContentResolver(),
                             Settings.Global.THEATER_MODE_ON, 1);
-                    if (interactive) {
+
+                    if (mGoToSleepOnButtonPressTheaterMode && interactive) {
                         mPowerManager.goToSleep(eventTime,
                                 PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                     }
@@ -1236,6 +1242,9 @@
         mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
 
+        mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode);
+
         mShortPressOnPowerBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_shortPressOnPowerBehavior);
         mLongPressOnPowerBehavior = mContext.getResources().getInteger(
@@ -2099,11 +2108,8 @@
     /** {@inheritDoc} */
     @Override
     public void removeStartingWindow(IBinder appToken, View window) {
-        if (DEBUG_STARTING_WINDOW) {
-            RuntimeException e = new RuntimeException("here");
-            e.fillInStackTrace();
-            Log.v(TAG, "Removing starting window for " + appToken + ": " + window, e);
-        }
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": "
+                + window + " Callers=" + Debug.getCallers(4));
 
         if (window != null) {
             WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -2299,24 +2305,19 @@
             boolean goingToNotificationShade) {
         if (goingToNotificationShade) {
             return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_behind_enter_fade_in);
-        } else if (onWallpaper) {
-            Animation a = AnimationUtils.loadAnimation(mContext,
-                    R.anim.lock_screen_behind_enter_wallpaper);
-            AnimationSet set = (AnimationSet) a;
-
-            // TODO: Use XML interpolators when we have log interpolators available in XML.
-            set.getAnimations().get(0).setInterpolator(mLogDecelerateInterpolator);
-            set.getAnimations().get(1).setInterpolator(mLogDecelerateInterpolator);
-            return set;
-        } else {
-            Animation a = AnimationUtils.loadAnimation(mContext,
-                    R.anim.lock_screen_behind_enter);
-            AnimationSet set = (AnimationSet) a;
-
-            // TODO: Use XML interpolators when we have log interpolators available in XML.
-            set.getAnimations().get(0).setInterpolator(mLogDecelerateInterpolator);
-            return set;
         }
+
+        AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(mContext, onWallpaper ?
+                    R.anim.lock_screen_behind_enter_wallpaper :
+                    R.anim.lock_screen_behind_enter);
+
+        // TODO: Use XML interpolators when we have log interpolators available in XML.
+        final List<Animation> animations = set.getAnimations();
+        for (int i = animations.size() - 1; i >= 0; --i) {
+            animations.get(i).setInterpolator(mLogDecelerateInterpolator);
+        }
+
+        return set;
     }
 
 
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 395e365..bb91a14 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -115,10 +115,10 @@
 
     @Override
     public boolean clearScores() {
-        // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
-        // be allowed to flush all scores.
+        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
+        // should be allowed to flush all scores.
         if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
-                mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
                         PackageManager.PERMISSION_GRANTED) {
             clearInternal();
             return true;
@@ -130,16 +130,16 @@
 
     @Override
     public boolean setActiveScorer(String packageName) {
-        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
         return setScorerInternal(packageName);
     }
 
     @Override
     public void disableScoring() {
-        // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETOWRKS) should
-        // be allowed to disable scoring.
+        // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
+        // should be allowed to disable scoring.
         if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
-                mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+                mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
                         PackageManager.PERMISSION_GRANTED) {
             // The return value is discarded here because at this point, the call should always
             // succeed. The only reason for failure is if the new package is not a valid scorer, but
@@ -188,7 +188,7 @@
 
     @Override
     public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
-        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
         synchronized (mScoreCaches) {
             if (mScoreCaches.containsKey(networkType)) {
                 throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index fc7b5a9..3ff9ff4 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -553,20 +553,22 @@
 
     private class DowntimeCallback implements DowntimeConditionProvider.Callback {
         @Override
-        public void onDowntimeChanged(boolean inDowntime) {
+        public void onDowntimeChanged(int downtimeMode) {
             final int mode = mZenModeHelper.getZenMode();
             final ZenModeConfig config = mZenModeHelper.getConfig();
-            // enter downtime
-            if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) {
+            final boolean inDowntime = downtimeMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                    || downtimeMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+            final boolean downtimeCurrent = mDowntime.isDowntimeCondition(mExitCondition);
+            // enter downtime, or update mode if reconfigured during an active downtime
+            if (inDowntime && (mode == Global.ZEN_MODE_OFF || downtimeCurrent)  && config != null) {
                 final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
                         Condition.STATE_TRUE);
-                mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter");
+                mZenModeHelper.setZenMode(downtimeMode, "downtimeEnter");
                 setZenModeCondition(condition, "downtime");
             }
             // exit downtime
-            if (!inDowntime && mDowntime.isDowntimeCondition(mExitCondition)
-                    && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                                || mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
+            if (!inDowntime && downtimeCurrent && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                    || mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
                 mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
             }
         }
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
index 2a4b718..881c9ad 100644
--- a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
+import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
 import android.service.notification.IConditionProvider;
@@ -64,7 +65,7 @@
     private final ArraySet<Integer> mDays = new ArraySet<Integer>();
 
     private boolean mConnected;
-    private boolean mInDowntime;
+    private int mDowntimeMode;
     private ZenModeConfig mConfig;
     private Callback mCallback;
 
@@ -75,7 +76,7 @@
     public void dump(PrintWriter pw, DumpFilter filter) {
         pw.println("    DowntimeConditionProvider:");
         pw.print("      mConnected="); pw.println(mConnected);
-        pw.print("      mInDowntime="); pw.println(mInDowntime);
+        pw.print("      mDowntimeMode="); pw.println(Global.zenModeToString(mDowntimeMode));
     }
 
     public void attachBase(Context base) {
@@ -113,7 +114,7 @@
     public void onRequestConditions(int relevance) {
         if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
         if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
-            if (mInDowntime && mConfig != null) {
+            if (isInDowntime() && mConfig != null) {
                 notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE));
             }
         }
@@ -124,7 +125,7 @@
         if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
         final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
         if (downtime != null && mConfig != null) {
-            final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime
+            final int state = mConfig.toDowntimeInfo().equals(downtime) && isInDowntime()
                     ? Condition.STATE_TRUE : Condition.STATE_FALSE;
             if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
             notifyCondition(createCondition(downtime, state));
@@ -146,7 +147,7 @@
     }
 
     public boolean isInDowntime() {
-        return mInDowntime;
+        return mDowntimeMode != Global.ZEN_MODE_OFF;
     }
 
     public Condition createCondition(DowntimeInfo downtime, int state) {
@@ -182,15 +183,18 @@
         }
     }
 
-    private boolean isInDowntime(long time) {
-        if (mConfig == null || mDays.size() == 0) return false;
+    private int computeDowntimeMode(long time) {
+        if (mConfig == null || mDays.size() == 0) return Global.ZEN_MODE_OFF;
         final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
         long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
-        if (start == end) return false;
+        if (start == end) return Global.ZEN_MODE_OFF;
         if (end < start) {
             end = addDays(end, 1);
         }
-        return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
+        final boolean inDowntime = isInDowntime(-1, time, start, end)
+                || isInDowntime(0, time, start, end);
+        return inDowntime ? (mConfig.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
+                : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) : Global.ZEN_MODE_OFF;
     }
 
     private boolean isInDowntime(int daysOffset, long time, long start, long end) {
@@ -202,18 +206,18 @@
     }
 
     private void reevaluateDowntime() {
-        final boolean inDowntime = isInDowntime(System.currentTimeMillis());
-        if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime);
-        if (inDowntime == mInDowntime) return;
-        Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime");
-        mInDowntime = inDowntime;
-        ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays);
+        final int downtimeMode = computeDowntimeMode(System.currentTimeMillis());
+        if (DEBUG) Slog.d(TAG, "downtimeMode=" + downtimeMode);
+        if (downtimeMode == mDowntimeMode) return;
+        mDowntimeMode = downtimeMode;
+        Slog.i(TAG, (isInDowntime() ? "Entering" : "Exiting" ) + " downtime");
+        ZenLog.traceDowntime(mDowntimeMode, getDayOfWeek(System.currentTimeMillis()), mDays);
         fireDowntimeChanged();
     }
 
     private void fireDowntimeChanged() {
         if (mCallback != null) {
-            mCallback.onDowntimeChanged(mInDowntime);
+            mCallback.onDowntimeChanged(mDowntimeMode);
         }
     }
 
@@ -256,7 +260,10 @@
             time = addDays(time, 1);
         }
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
-                new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+                new Intent(action)
+                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                    .putExtra(EXTRA_TIME, time),
+                PendingIntent.FLAG_UPDATE_CURRENT);
         alarms.cancel(pendingIntent);
         if (mConfig.sleepMode != null) {
             if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
@@ -290,6 +297,6 @@
     };
 
     public interface Callback {
-        void onDowntimeChanged(boolean inDowntime);
+        void onDowntimeChanged(int downtimeMode);
     }
 }
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index e0b83de..1a3da79 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -75,8 +75,8 @@
         append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode));
     }
 
-    public static void traceDowntime(boolean inDowntime, int day, ArraySet<Integer> days) {
-        append(TYPE_DOWNTIME, inDowntime + ",day=" + day + ",days=" + days);
+    public static void traceDowntime(int downtimeMode, int day, ArraySet<Integer> days) {
+        append(TYPE_DOWNTIME, zenModeToString(downtimeMode) + ",day=" + day + ",days=" + days);
     }
 
     public static void traceSetZenMode(int mode, String reason) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index bfc7659..eeb007c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -691,8 +691,10 @@
                 throw new RuntimeException("Invalid thumbnail transition state");
         }
 
-        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
-                THUMBNAIL_APP_TRANSITION_DURATION, mThumbnailFastOutSlowInInterpolator);
+        int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
+                THUMBNAIL_APP_TRANSITION_DURATION);
+        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
+                mThumbnailFastOutSlowInInterpolator);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 69c9144..bf96ea5 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -58,7 +58,9 @@
     // the state changes.
     boolean allDrawn;
 
-    // Special surface for thumbnail animation.
+    // Special surface for thumbnail animation.  If deferThumbnailDestruction is enabled, then we
+    // will make sure that the thumbnail is destroyed after the other surface is completed.  This
+    // requires that the duration of the two animations are the same.
     SurfaceControl thumbnail;
     int thumbnailTransactionSeq;
     int thumbnailX;
@@ -68,13 +70,12 @@
     Animation thumbnailAnimation;
     final Transformation thumbnailTransformation = new Transformation();
     // This flag indicates that the destruction of the thumbnail surface is synchronized with
-    // another animation, so do not pre-emptively destroy the thumbnail surface when the animation
-    // completes
+    // another animation, so defer the destruction of this thumbnail surface for a single frame
+    // after the secondary animation completes.
     boolean deferThumbnailDestruction;
-    // This is the thumbnail surface that has been bestowed upon this animator, and when the
-    // surface for this animator's animation is complete, we will destroy the thumbnail surface
-    // as well.  Do not animate or do anything with this surface.
-    SurfaceControl deferredThumbnail;
+    // This flag is set if the animator has deferThumbnailDestruction set and has reached the final
+    // frame of animation.  It will extend the animation by one frame and then clean up afterwards.
+    boolean deferFinalFrameCleanup;
 
     /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
     ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<WindowStateAnimator>();
@@ -134,9 +135,7 @@
             animation = null;
             animating = true;
         }
-        if (!deferThumbnailDestruction) {
-            clearThumbnail();
-        }
+        clearThumbnail();
         if (mAppToken.deferClearAllDrawn) {
             mAppToken.allDrawn = false;
             mAppToken.deferClearAllDrawn = false;
@@ -148,13 +147,7 @@
             thumbnail.destroy();
             thumbnail = null;
         }
-    }
-
-    public void clearDeferredThumbnail() {
-        if (deferredThumbnail != null) {
-            deferredThumbnail.destroy();
-            deferredThumbnail = null;
-        }
+        deferThumbnailDestruction = false;
     }
 
     void updateLayers() {
@@ -223,19 +216,26 @@
             return false;
         }
         transformation.clear();
-        final boolean more = animation.getTransformation(currentTime, transformation);
-        if (false && WindowManagerService.DEBUG_ANIM) Slog.v(
-            TAG, "Stepped animation in " + mAppToken + ": more=" + more + ", xform=" + transformation);
-        if (!more) {
-            animation = null;
-            if (!deferThumbnailDestruction) {
+        boolean hasMoreFrames = animation.getTransformation(currentTime, transformation);
+        if (!hasMoreFrames) {
+            if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
+                // We are deferring the thumbnail destruction, so extend the animation for one more
+                // (dummy) frame before we clean up
+                deferFinalFrameCleanup = true;
+                hasMoreFrames = true;
+            } else {
+                if (false && WindowManagerService.DEBUG_ANIM) Slog.v(
+                        TAG, "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
+                                ", xform=" + transformation);
+                deferFinalFrameCleanup = false;
+                animation = null;
                 clearThumbnail();
+                if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                        TAG, "Finished animation in " + mAppToken + " @ " + currentTime);
             }
-            if (WindowManagerService.DEBUG_ANIM) Slog.v(
-                TAG, "Finished animation in " + mAppToken + " @ " + currentTime);
         }
-        hasTransformation = more;
-        return more;
+        hasTransformation = hasMoreFrames;
+        return hasMoreFrames;
     }
 
     // This must be called while inside a transaction.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 61ea1e8..8d93141 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -91,6 +91,9 @@
     boolean mKeyguardGoingAwayToNotificationShade;
     boolean mKeyguardGoingAwayDisableWindowAnimations;
 
+    /** Use one animation for all entering activities after keyguard is dismissed. */
+    Animation mPostKeyguardExitAnimation;
+
     // forceHiding states.
     static final int KEYGUARD_NOT_SHOWN     = 0;
     static final int KEYGUARD_ANIMATING_IN  = 1;
@@ -220,9 +223,6 @@
         ++mAnimTransactionSequence;
 
         final WindowList windows = mService.getWindowListLocked(displayId);
-        ArrayList<WindowStateAnimator> unForceHiding = null;
-        boolean wallpaperInUnForceHiding = false;
-        WindowState wallpaper = null;
 
         if (mKeyguardGoingAway) {
             for (int i = windows.size() - 1; i >= 0; i--) {
@@ -261,6 +261,9 @@
         final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
                 null : winShowWhenLocked.mAppToken;
 
+        boolean wallpaperInUnForceHiding = false;
+        ArrayList<WindowStateAnimator> unForceHiding = null;
+        WindowState wallpaper = null;
         for (int i = windows.size() - 1; i >= 0; i--) {
             WindowState win = windows.get(i);
             WindowStateAnimator winAnimator = win.mWinAnimator;
@@ -327,40 +330,53 @@
                 } else if (mPolicy.canBeForceHidden(win, win.mAttrs)) {
                     final boolean hideWhenLocked = !((win.mIsImWindow && showImeOverKeyguard) ||
                             (appShowWhenLocked != null && appShowWhenLocked == win.mAppToken));
-                    final boolean changed;
                     if (((mForceHiding == KEYGUARD_ANIMATING_IN)
                                 && (!winAnimator.isAnimating() || hideWhenLocked))
                             || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) {
-                        changed = win.hideLw(false, false);
-                        if ((DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY)
-                                && changed) Slog.v(TAG, "Now policy hidden: " + win);
+                        if (!win.hideLw(false, false)) {
+                            // Was already hidden
+                            continue;
+                        }
+                        if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                                "Now policy hidden: " + win);
                     } else {
-                        changed = win.showLw(false, false);
-                        if ((DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY)
-                                && changed) Slog.v(TAG, "Now policy shown: " + win);
-                        if (changed) {
-                            if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
-                                    && win.isVisibleNow() /*w.isReadyForDisplay()*/) {
-                                if (unForceHiding == null) {
-                                    unForceHiding = new ArrayList<WindowStateAnimator>();
-                                }
-                                unForceHiding.add(winAnimator);
-                                if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
-                                    wallpaperInUnForceHiding = true;
-                                }
+                        if (!win.showLw(false, false)) {
+                            // Was already showing.
+                            continue;
+                        }
+                        final boolean visibleNow = win.isVisibleNow();
+                        if (!visibleNow) {
+                            // Couldn't really show, must showLw() again when win becomes visible.
+                            win.hideLw(false, false);
+                            continue;
+                        }
+                        if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                                "Now policy shown: " + win);
+                        if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0) {
+                            if (unForceHiding == null) {
+                                unForceHiding = new ArrayList<>();
                             }
-                            final WindowState currentFocus = mService.mCurrentFocus;
-                            if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
-                                // We are showing on to of the current
-                                // focus, so re-evaluate focus to make
-                                // sure it is correct.
-                                if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.v(TAG,
-                                        "updateWindowsLocked: setting mFocusMayChange true");
-                                mService.mFocusMayChange = true;
+                            unForceHiding.add(winAnimator);
+                            if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
+                                wallpaperInUnForceHiding = true;
                             }
+                        } else if (mPostKeyguardExitAnimation != null) {
+                            // We're already in the middle of an animation. Use the existing
+                            // animation to bring in this window.
+                            winAnimator.setAnimation(mPostKeyguardExitAnimation);
+                            winAnimator.keyguardGoingAwayAnimation = true;
+                        }
+                        final WindowState currentFocus = mService.mCurrentFocus;
+                        if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
+                            // We are showing on top of the current
+                            // focus, so re-evaluate focus to make
+                            // sure it is correct.
+                            if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.v(TAG,
+                                    "updateWindowsLocked: setting mFocusMayChange true");
+                            mService.mFocusMayChange = true;
                         }
                     }
-                    if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) {
+                    if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
                         mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
                         setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
@@ -403,42 +419,44 @@
         // If we have windows that are being show due to them no longer
         // being force-hidden, apply the appropriate animation to them.
         if (unForceHiding != null) {
-            boolean startKeyguardExit = true;
-            for (int i=unForceHiding.size()-1; i>=0; i--) {
-                Animation a = null;
-                if (!mKeyguardGoingAwayDisableWindowAnimations) {
-                    a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding,
-                            mKeyguardGoingAwayToNotificationShade);
-                    if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: created anim=" + a
-                            + " for win=" + unForceHiding.get(i));
-                } else {
-                    if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: skipping anim for win="
-                            + unForceHiding.get(i));
-                }
-                if (a != null) {
+            // This only happens the first time that we detect the keyguard is animating out.
+            if (mKeyguardGoingAwayDisableWindowAnimations) {
+                if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: skipping anim for windows");
+            } else {
+                if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: created anim for windows");
+                mPostKeyguardExitAnimation = mPolicy.createForceHideEnterAnimation(
+                        wallpaperInUnForceHiding, mKeyguardGoingAwayToNotificationShade);
+            }
+            if (mPostKeyguardExitAnimation != null) {
+                for (int i=unForceHiding.size()-1; i>=0; i--) {
                     final WindowStateAnimator winAnimator = unForceHiding.get(i);
-                    winAnimator.setAnimation(a);
+                    winAnimator.setAnimation(mPostKeyguardExitAnimation);
                     winAnimator.keyguardGoingAwayAnimation = true;
-                    if (startKeyguardExit && mKeyguardGoingAway) {
-                        // Do one time only.
-                        mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
-                                a.getDuration());
-                        mKeyguardGoingAway = false;
-                        startKeyguardExit = false;
-                    }
                 }
             }
+        }
 
-            // Wallpaper is going away in un-force-hide motion, animate it as well.
-            if (!wallpaperInUnForceHiding && wallpaper != null
-                    && !mKeyguardGoingAwayDisableWindowAnimations) {
-                if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
-                Animation a = mPolicy.createForceHideWallpaperExitAnimation(
-                        mKeyguardGoingAwayToNotificationShade);
-                if (a != null) {
-                    WindowStateAnimator animator = wallpaper.mWinAnimator;
-                    animator.setAnimation(a);
-                }
+        if (mPostKeyguardExitAnimation != null) {
+            // We're in the midst of a keyguard exit animation.
+            if (mKeyguardGoingAway) {
+                mPolicy.startKeyguardExitAnimation(mCurrentTime +
+                        mPostKeyguardExitAnimation.getStartOffset(),
+                        mPostKeyguardExitAnimation.getDuration());
+                mKeyguardGoingAway = false;
+            } else if (mPostKeyguardExitAnimation.hasEnded()) {
+                // Done with the animation, reset.
+                mPostKeyguardExitAnimation = null;
+            }
+        }
+
+        // Wallpaper is going away in un-force-hide motion, animate it as well.
+        if (!wallpaperInUnForceHiding && wallpaper != null
+                && !mKeyguardGoingAwayDisableWindowAnimations) {
+            if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
+            Animation a = mPolicy.createForceHideWallpaperExitAnimation(
+                    mKeyguardGoingAwayToNotificationShade);
+            if (a != null) {
+                wallpaper.mWinAnimator.setAnimation(a);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b4e2778..968b35c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9244,12 +9244,6 @@
                                 topClosingLayer);
                         openingAppAnimator.deferThumbnailDestruction =
                                 !mAppTransition.isNextThumbnailTransitionScaleUp();
-                        if (openingAppAnimator.deferThumbnailDestruction) {
-                            if (closingAppAnimator != null &&
-                                    closingAppAnimator.animation != null) {
-                                closingAppAnimator.deferredThumbnail = surfaceControl;
-                            }
-                        }
                     } else {
                         anim = mAppTransition.createThumbnailScaleAnimationLocked(
                                 displayInfo.appWidth, displayInfo.appHeight, transit);
@@ -9783,7 +9777,7 @@
                                     if (!w.isDrawnLw()) {
                                         Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceControl
                                                 + " pv=" + w.mPolicyVisibility
-                                                + " mDrawState=" + winAnimator.mDrawState
+                                                + " mDrawState=" + winAnimator.drawStateToString()
                                                 + " ah=" + w.mAttachedHidden
                                                 + " th=" + atoken.hiddenRequested
                                                 + " a=" + winAnimator.mAnimating);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fc3f2c2..1dadb17 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -167,14 +167,14 @@
     private static final int SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN =
             View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 
-    static String drawStateToString(int state) {
-        switch (state) {
+    String drawStateToString() {
+        switch (mDrawState) {
             case NO_SURFACE: return "NO_SURFACE";
             case DRAW_PENDING: return "DRAW_PENDING";
             case COMMIT_DRAW_PENDING: return "COMMIT_DRAW_PENDING";
             case READY_TO_SHOW: return "READY_TO_SHOW";
             case HAS_DRAWN: return "HAS_DRAWN";
-            default: return Integer.toString(state);
+            default: return Integer.toString(mDrawState);
         }
     }
     int mDrawState;
@@ -489,7 +489,7 @@
         if (DEBUG_STARTING_WINDOW &&
                 mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
             Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
-                    + drawStateToString(mDrawState));
+                    + drawStateToString());
         }
         if (mDrawState == DRAW_PENDING) {
             if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
@@ -510,18 +510,17 @@
         if (DEBUG_STARTING_WINDOW &&
                 mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
             Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState="
-                    + drawStateToString(mDrawState));
+                    + drawStateToString());
         }
-        if (mDrawState != COMMIT_DRAW_PENDING) {
+        if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
             return false;
         }
         if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
             Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
         }
         mDrawState = READY_TO_SHOW;
-        final boolean starting = mWin.mAttrs.type == TYPE_APPLICATION_STARTING;
         final AppWindowToken atoken = mWin.mAppToken;
-        if (atoken == null || atoken.allDrawn || starting) {
+        if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
             performShowLocked();
         }
         return true;
@@ -976,11 +975,6 @@
             mWin.mHasSurface = false;
             mDrawState = NO_SURFACE;
         }
-
-        // Destroy any deferred thumbnail surfaces
-        if (mAppAnimator != null) {
-            mAppAnimator.clearDeferredThumbnail();
-        }
     }
 
     void destroyDeferredSurfaceLocked() {
@@ -1868,7 +1862,7 @@
             if (dumpAll) {
                 pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
                 pw.print(prefix); pw.print("mDrawState=");
-                pw.print(drawStateToString(mDrawState));
+                pw.print(drawStateToString());
                 pw.print(" mLastHidden="); pw.println(mLastHidden);
             }
             pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index f447462..848d9a1 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -794,4 +794,23 @@
     return a.diff(b) == axisMask;
 }
 
+bool isDensityOnly(const ResTable_config& config) {
+    if (config.density == ResTable_config::DENSITY_NONE) {
+        return false;
+    }
+
+    if (config.density == ResTable_config::DENSITY_ANY) {
+        if (config.sdkVersion != SDK_L) {
+            // Someone modified the sdkVersion from the default, this is not safe to assume.
+            return false;
+        }
+    } else if (config.sdkVersion != SDK_DONUT) {
+        return false;
+    }
+
+    const uint32_t mask = ResTable_config::CONFIG_DENSITY | ResTable_config::CONFIG_VERSION;
+    const ConfigDescription nullConfig;
+    return (nullConfig.diff(config) & ~mask) == 0;
+}
+
 } // namespace AaptConfig
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
index 2963539..f73a5081 100644
--- a/tools/aapt/AaptConfig.h
+++ b/tools/aapt/AaptConfig.h
@@ -80,6 +80,12 @@
  */
 bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask);
 
+/**
+ * Returns true if the configuration only has the density specified. In the case
+ * of 'anydpi', the version is ignored.
+ */
+bool isDensityOnly(const android::ResTable_config& config);
+
 } // namespace AaptConfig
 
 #endif // __AAPT_CONFIG_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index b9bd03a..0d8db13 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -4,6 +4,7 @@
 // Build resource files from raw assets.
 //
 #include "AaptAssets.h"
+#include "AaptUtil.h"
 #include "AaptXml.h"
 #include "CacheUpdater.h"
 #include "CrunchCache.h"
@@ -13,9 +14,12 @@
 #include "Main.h"
 #include "ResourceTable.h"
 #include "StringPool.h"
+#include "Symbol.h"
 #include "WorkQueue.h"
 #include "XMLNode.h"
 
+#include <algorithm>
+
 #if HAVE_PRINTF_ZD
 #  define ZD "%zd"
 #  define ZD_TYPE ssize_t
@@ -1550,6 +1554,7 @@
     // Re-flatten because we may have added new resource IDs
     // --------------------------------------------------------------
 
+
     ResTable finalResTable;
     sp<AaptFile> resFile;
     
@@ -1560,6 +1565,13 @@
             return err;
         }
 
+        KeyedVector<Symbol, Vector<SymbolDefinition> > densityVaryingResources;
+        if (builder->getSplits().size() > 1) {
+            // Only look for density varying resources if we're generating
+            // splits.
+            table.getDensityVaryingResources(densityVaryingResources);
+        }
+
         Vector<sp<ApkSplit> >& splits = builder->getSplits();
         const size_t numSplits = splits.size();
         for (size_t i = 0; i < numSplits; i++) {
@@ -1583,6 +1595,63 @@
                     return err;
                 }
             } else {
+                ResTable resTable;
+                err = resTable.add(flattenedTable->getData(), flattenedTable->getSize());
+                if (err != NO_ERROR) {
+                    fprintf(stderr, "Generated resource table for split '%s' is corrupt.\n",
+                            split->getPrintableName().string());
+                    return err;
+                }
+
+                bool hasError = false;
+                const std::set<ConfigDescription>& splitConfigs = split->getConfigs();
+                for (std::set<ConfigDescription>::const_iterator iter = splitConfigs.begin();
+                        iter != splitConfigs.end();
+                        ++iter) {
+                    const ConfigDescription& config = *iter;
+                    if (AaptConfig::isDensityOnly(config)) {
+                        // Each density only split must contain all
+                        // density only resources.
+                        Res_value val;
+                        resTable.setParameters(&config);
+                        const size_t densityVaryingResourceCount = densityVaryingResources.size();
+                        for (size_t k = 0; k < densityVaryingResourceCount; k++) {
+                            const Symbol& symbol = densityVaryingResources.keyAt(k);
+                            ssize_t block = resTable.getResource(symbol.id, &val, true);
+                            if (block < 0) {
+                                // Maybe it's in the base?
+                                finalResTable.setParameters(&config);
+                                block = finalResTable.getResource(symbol.id, &val, true);
+                            }
+
+                            if (block < 0) {
+                                hasError = true;
+                                SourcePos().error("%s has no definition for density split '%s'",
+                                        symbol.toString().string(), config.toString().string());
+
+                                if (bundle->getVerbose()) {
+                                    const Vector<SymbolDefinition>& defs = densityVaryingResources[k];
+                                    const size_t defCount = std::min(size_t(5), defs.size());
+                                    for (size_t d = 0; d < defCount; d++) {
+                                        const SymbolDefinition& def = defs[d];
+                                        def.source.error("%s has definition for %s",
+                                                symbol.toString().string(), def.config.toString().string());
+                                    }
+
+                                    if (defCount < defs.size()) {
+                                        SourcePos().error("and %d more ...", (int) (defs.size() - defCount));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (hasError) {
+                    return UNKNOWN_ERROR;
+                }
+
+                // Generate the AndroidManifest for this split.
                 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
                         AaptGroupEntry(), String8());
                 err = generateAndroidManifestForSplit(bundle, assets, split,
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0f94f85..beff604 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -6,6 +6,7 @@
 
 #include "ResourceTable.h"
 
+#include "AaptUtil.h"
 #include "XMLNode.h"
 #include "ResourceFilter.h"
 #include "ResourceIdCache.h"
@@ -4486,3 +4487,34 @@
 
     return NO_ERROR;
 }
+
+void ResourceTable::getDensityVaryingResources(KeyedVector<Symbol, Vector<SymbolDefinition> >& resources) {
+    const ConfigDescription nullConfig;
+
+    const size_t packageCount = mOrderedPackages.size();
+    for (size_t p = 0; p < packageCount; p++) {
+        const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes();
+        const size_t typeCount = types.size();
+        for (size_t t = 0; t < typeCount; t++) {
+            const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs();
+            const size_t configCount = configs.size();
+            for (size_t c = 0; c < configCount; c++) {
+                const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries = configs[c]->getEntries();
+                const size_t configEntryCount = configEntries.size();
+                for (size_t ce = 0; ce < configEntryCount; ce++) {
+                    const ConfigDescription& config = configEntries.keyAt(ce);
+                    if (AaptConfig::isDensityOnly(config)) {
+                        // This configuration only varies with regards to density.
+                        const Symbol symbol(mOrderedPackages[p]->getName(),
+                                types[t]->getName(),
+                                configs[c]->getName(),
+                                getResId(mOrderedPackages[p], types[t], configs[c]->getEntryIndex()));
+
+                        const sp<Entry>& entry = configEntries.valueAt(ce);
+                        AaptUtil::appendValue(resources, symbol, SymbolDefinition(symbol, config, entry->getPos()));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index eac5dd3..db392c89 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -7,15 +7,16 @@
 #ifndef RESOURCE_TABLE_H
 #define RESOURCE_TABLE_H
 
-#include "ConfigDescription.h"
-#include "StringPool.h"
-#include "SourcePos.h"
-#include "ResourceFilter.h"
-
 #include <map>
 #include <queue>
 #include <set>
 
+#include "ConfigDescription.h"
+#include "ResourceFilter.h"
+#include "SourcePos.h"
+#include "StringPool.h"
+#include "Symbol.h"
+
 using namespace std;
 
 class XMLNode;
@@ -543,6 +544,8 @@
         DefaultKeyedVector<String16, uint32_t> mKeyStringsMapping;
     };
 
+    void getDensityVaryingResources(KeyedVector<Symbol, Vector<SymbolDefinition> >& resources);
+
 private:
     void writePublicDefinitions(const String16& package, FILE* fp, bool pub);
     sp<Package> getPackage(const String16& package);
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index ae25047..3864320 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -141,6 +141,12 @@
 }
 
 bool
+SourcePos::operator<(const SourcePos& rhs) const
+{
+    return (file < rhs.file) || (line < rhs.line);
+}
+
+bool
 SourcePos::hasErrors()
 {
     return g_errors.size() > 0;
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
index 4ce817f..13cfb9d 100644
--- a/tools/aapt/SourcePos.h
+++ b/tools/aapt/SourcePos.h
@@ -21,6 +21,8 @@
     void warning(const char* fmt, ...) const;
     void printf(const char* fmt, ...) const;
 
+    bool operator<(const SourcePos& rhs) const;
+
     static bool hasErrors();
     static void printErrors(FILE* to);
 };
diff --git a/tools/aapt/Symbol.h b/tools/aapt/Symbol.h
new file mode 100644
index 0000000..e157541
--- /dev/null
+++ b/tools/aapt/Symbol.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_SYMBOL_H
+#define AAPT_SYMBOL_H
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include "ConfigDescription.h"
+#include "SourcePos.h"
+
+/**
+ * A resource symbol, not attached to any configuration or context.
+ */
+struct Symbol {
+    inline Symbol();
+    inline Symbol(const android::String16& p, const android::String16& t, const android::String16& n, uint32_t i);
+    inline android::String8 toString() const;
+    inline bool operator<(const Symbol& rhs) const;
+
+    android::String16 package;
+    android::String16 type;
+    android::String16 name;
+    uint32_t id;
+
+};
+
+/**
+ * A specific defintion of a symbol, defined with a configuration and a definition site.
+ */
+struct SymbolDefinition {
+    inline SymbolDefinition();
+    inline SymbolDefinition(const Symbol& s, const ConfigDescription& c, const SourcePos& src);
+    inline bool operator<(const SymbolDefinition& rhs) const;
+
+    Symbol symbol;
+    ConfigDescription config;
+    SourcePos source;
+};
+
+//
+// Implementations
+//
+
+Symbol::Symbol() {
+}
+
+Symbol::Symbol(const android::String16& p, const android::String16& t, const android::String16& n, uint32_t i)
+    : package(p)
+    , type(t)
+    , name(n)
+    , id(i) {
+}
+
+android::String8 Symbol::toString() const {
+    return android::String8::format("%s:%s/%s (0x%08x)",
+            android::String8(package).string(),
+            android::String8(type).string(),
+            android::String8(name).string(),
+            (int) id);
+}
+
+bool Symbol::operator<(const Symbol& rhs) const {
+    return (package < rhs.package) || (type < rhs.type) || (name < rhs.name) || (id < rhs.id);
+}
+
+SymbolDefinition::SymbolDefinition() {
+}
+
+SymbolDefinition::SymbolDefinition(const Symbol& s, const ConfigDescription& c, const SourcePos& src)
+    : symbol(s)
+    , config(c)
+    , source(src) {
+}
+
+bool SymbolDefinition::operator<(const SymbolDefinition& rhs) const {
+    return (symbol < rhs.symbol) || (config < rhs.config) || (source < rhs.source);
+}
+
+#endif // AAPT_SYMBOL_H
+