Merge "Fix QS FBE-ness" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index f50fc30..a5d8a93 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -944,6 +944,8 @@
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
+    field public static final int popupEnterTransition = 16844065; // 0x1010521
+    field public static final int popupExitTransition = 16844066; // 0x1010522
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -2754,14 +2756,10 @@
   }
 
   public final class GestureDescription {
-    method public static android.accessibilityservice.GestureDescription createClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
-    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public static long getMaxGestureDuration();
+    method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
     method public int getStrokeCount();
-    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
-    field public static final int MAX_STROKE_COUNT = 10; // 0xa
   }
 
   public static class GestureDescription.Builder {
@@ -8019,8 +8017,6 @@
     method public abstract java.lang.String getPackageResourcePath();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
-    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
-    method public abstract java.io.File getSharedPreferencesPath(java.lang.String);
     method public final java.lang.String getString(int);
     method public final java.lang.String getString(int, java.lang.Object...);
     method public abstract java.lang.Object getSystemService(java.lang.String);
@@ -29598,6 +29594,7 @@
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
+    method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
@@ -47197,6 +47194,8 @@
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
     method public float getElevation();
+    method public android.transition.Transition getEnterTransition();
+    method public android.transition.Transition getExitTransition();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -50690,6 +50689,7 @@
     method public float floatValue();
     method public static int hashCode(double);
     method public int intValue();
+    method public static boolean isFinite(double);
     method public static boolean isInfinite(double);
     method public boolean isInfinite();
     method public static boolean isNaN(double);
@@ -50771,6 +50771,7 @@
     method public float floatValue();
     method public static float intBitsToFloat(int);
     method public int intValue();
+    method public static boolean isFinite(float);
     method public static boolean isInfinite(float);
     method public boolean isInfinite();
     method public static boolean isNaN(float);
diff --git a/api/removed.txt b/api/removed.txt
index 9b5d3ab..36c8ce5 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -22,6 +22,8 @@
   public abstract class Context {
     method public deprecated android.content.Context createCredentialEncryptedStorageContext();
     method public deprecated android.content.Context createDeviceEncryptedStorageContext();
+    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
+    method public abstract java.io.File getSharedPreferencesPath(java.lang.String);
     method public deprecated boolean isCredentialEncryptedStorage();
     method public deprecated boolean isDeviceEncryptedStorage();
     method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 00603bb..97b7710 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1039,6 +1039,8 @@
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
+    field public static final int popupEnterTransition = 16844065; // 0x1010521
+    field public static final int popupExitTransition = 16844066; // 0x1010522
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -2856,14 +2858,10 @@
   }
 
   public final class GestureDescription {
-    method public static android.accessibilityservice.GestureDescription createClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
-    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public static long getMaxGestureDuration();
+    method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
     method public int getStrokeCount();
-    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
-    field public static final int MAX_STROKE_COUNT = 10; // 0xa
   }
 
   public static class GestureDescription.Builder {
@@ -8318,8 +8316,6 @@
     method public abstract java.lang.String getPackageResourcePath();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
-    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
-    method public abstract java.io.File getSharedPreferencesPath(java.lang.String);
     method public final java.lang.String getString(int);
     method public final java.lang.String getString(int, java.lang.Object...);
     method public abstract java.lang.Object getSystemService(java.lang.String);
@@ -31911,6 +31907,7 @@
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
+    method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
@@ -50261,6 +50258,8 @@
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
     method public float getElevation();
+    method public android.transition.Transition getEnterTransition();
+    method public android.transition.Transition getExitTransition();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -53754,6 +53753,7 @@
     method public float floatValue();
     method public static int hashCode(double);
     method public int intValue();
+    method public static boolean isFinite(double);
     method public static boolean isInfinite(double);
     method public boolean isInfinite();
     method public static boolean isNaN(double);
@@ -53835,6 +53835,7 @@
     method public float floatValue();
     method public static float intBitsToFloat(int);
     method public int intValue();
+    method public static boolean isFinite(float);
     method public static boolean isInfinite(float);
     method public boolean isInfinite();
     method public static boolean isNaN(float);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 8bf59df..d48b9b3 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -20,6 +20,8 @@
   public abstract class Context {
     method public deprecated android.content.Context createCredentialEncryptedStorageContext();
     method public deprecated android.content.Context createDeviceEncryptedStorageContext();
+    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
+    method public abstract java.io.File getSharedPreferencesPath(java.lang.String);
     method public deprecated boolean isCredentialEncryptedStorage();
     method public deprecated boolean isDeviceEncryptedStorage();
     method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index c4b01db..fa2669c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -944,6 +944,8 @@
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
+    field public static final int popupEnterTransition = 16844065; // 0x1010521
+    field public static final int popupExitTransition = 16844066; // 0x1010522
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -2754,14 +2756,10 @@
   }
 
   public final class GestureDescription {
-    method public static android.accessibilityservice.GestureDescription createClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
-    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public static long getMaxGestureDuration();
+    method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
     method public int getStrokeCount();
-    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
-    field public static final int MAX_STROKE_COUNT = 10; // 0xa
   }
 
   public static class GestureDescription.Builder {
@@ -8024,8 +8022,6 @@
     method public abstract java.lang.String getPackageResourcePath();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
-    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
-    method public abstract java.io.File getSharedPreferencesPath(java.lang.String);
     method public final java.lang.String getString(int);
     method public final java.lang.String getString(int, java.lang.Object...);
     method public abstract java.lang.Object getSystemService(java.lang.String);
@@ -29664,6 +29660,7 @@
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
+    method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
@@ -47270,6 +47267,8 @@
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
     method public float getElevation();
+    method public android.transition.Transition getEnterTransition();
+    method public android.transition.Transition getExitTransition();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -50763,6 +50762,7 @@
     method public float floatValue();
     method public static int hashCode(double);
     method public int intValue();
+    method public static boolean isFinite(double);
     method public static boolean isInfinite(double);
     method public boolean isInfinite();
     method public static boolean isNaN(double);
@@ -50844,6 +50844,7 @@
     method public float floatValue();
     method public static float intBitsToFloat(int);
     method public int intValue();
+    method public static boolean isFinite(float);
     method public static boolean isInfinite(float);
     method public boolean isInfinite();
     method public static boolean isNaN(float);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 9b5d3ab..36c8ce5 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -22,6 +22,8 @@
   public abstract class Context {
     method public deprecated android.content.Context createCredentialEncryptedStorageContext();
     method public deprecated android.content.Context createDeviceEncryptedStorageContext();
+    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
+    method public abstract java.io.File getSharedPreferencesPath(java.lang.String);
     method public deprecated boolean isCredentialEncryptedStorage();
     method public deprecated boolean isDeviceEncryptedStorage();
     method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ac3b8e3..ddc5b0c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -851,6 +851,7 @@
                     return connection.getMagnificationScale();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain scale", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return 1.0f;
@@ -879,6 +880,7 @@
                     return connection.getMagnificationCenterX();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain center X", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return 0.0f;
@@ -907,6 +909,7 @@
                     return connection.getMagnificationCenterY();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain center Y", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return 0.0f;
@@ -933,6 +936,7 @@
                     return connection.getMagnifiedRegion();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain magnified region", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return Region.obtain();
@@ -961,6 +965,7 @@
                     return connection.resetMagnification(animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to reset", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return false;
@@ -989,6 +994,7 @@
                             scale, Float.NaN, Float.NaN, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to set scale", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return false;
@@ -1020,6 +1026,7 @@
                             Float.NaN, centerX, centerY, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to set center", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return false;
@@ -1254,10 +1261,7 @@
                    Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
                    re.rethrowFromSystemServer();
                }
-           } else {
-               throw new RuntimeException("AccessibilityServiceConnection is null");
            }
-
            return false;
         }
 
@@ -1301,6 +1305,7 @@
                 return connection.performGlobalAction(action);
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
+                re.rethrowFromSystemServer();
             }
         }
         return false;
@@ -1349,6 +1354,7 @@
                 return connection.getServiceInfo();
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
+                re.rethrowFromSystemServer();
             }
         }
         return null;
@@ -1382,6 +1388,7 @@
                 AccessibilityInteractionClient.getInstance().clearCache();
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
+                re.rethrowFromSystemServer();
             }
         }
     }
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index 14aabcf..7a0c89b 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -43,135 +43,32 @@
  */
 public final class GestureDescription {
     /** Gestures may contain no more than this many strokes */
-    public static final int MAX_STROKE_COUNT = 10;
+    private static final int MAX_STROKE_COUNT = 10;
 
     /**
      * Upper bound on total gesture duration. Nearly all gestures will be much shorter.
      */
-    public static final long MAX_GESTURE_DURATION_MS = 60 * 1000;
+    private static final long MAX_GESTURE_DURATION_MS = 60 * 1000;
 
     private final List<StrokeDescription> mStrokes = new ArrayList<>();
     private final float[] mTempPos = new float[2];
 
     /**
-     * Create a description of a click gesture
+     * Get the upper limit for the number of strokes a gesture may contain.
      *
-     * @param x The x coordinate to click. Must not be negative.
-     * @param y The y coordinate to click. Must not be negative.
-     *
-     * @return A description of a click at (x, y)
+     * @return The maximum number of strokes.
      */
-    public static GestureDescription createClick(@IntRange(from = 0) int x,
-            @IntRange(from = 0) int y) {
-        Path clickPath = new Path();
-        clickPath.moveTo(x, y);
-        clickPath.lineTo(x + 1, y);
-        return new GestureDescription(
-                new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout()));
+    public static int getMaxStrokeCount() {
+        return MAX_STROKE_COUNT;
     }
 
     /**
-     * Create a description of a long click gesture
+     * Get the upper limit on a gesture's duration.
      *
-     * @param x The x coordinate to click. Must not be negative.
-     * @param y The y coordinate to click. Must not be negative.
-     *
-     * @return A description of a click at (x, y)
+     * @return The maximum duration in milliseconds.
      */
-    public static GestureDescription createLongClick(@IntRange(from = 0) int x,
-            @IntRange(from = 0) int y) {
-        Path clickPath = new Path();
-        clickPath.moveTo(x, y);
-        clickPath.lineTo(x + 1, y);
-        int longPressTime = ViewConfiguration.getLongPressTimeout();
-        return new GestureDescription(
-                new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2)));
-    }
-
-    /**
-     * Create a description of a swipe gesture
-     *
-     * @param startX The x coordinate of the starting point. Must not be negative.
-     * @param startY The y coordinate of the starting point. Must not be negative.
-     * @param endX The x coordinate of the ending point. Must not be negative.
-     * @param endY The y coordinate of the ending point. Must not be negative.
-     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
-     *
-     * @return A description of a swipe from ({@code startX}, {@code startY}) to
-     * ({@code endX}, {@code endY}) that takes {@code duration} milliseconds. Returns {@code null}
-     * if the path specified for the swipe is invalid.
-     */
-    public static GestureDescription createSwipe(@IntRange(from = 0) int startX,
-            @IntRange(from = 0) int startY,
-            @IntRange(from = 0) int endX,
-            @IntRange(from = 0) int endY,
-            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
-        Path swipePath = new Path();
-        swipePath.moveTo(startX, startY);
-        swipePath.lineTo(endX, endY);
-        return new GestureDescription(new StrokeDescription(swipePath, 0, duration));
-    }
-
-    /**
-     * Create a description for a pinch (or zoom) gesture.
-     *
-     * @param centerX The x coordinate of the center of the pinch. Must not be negative.
-     * @param centerY The y coordinate of the center of the pinch. Must not be negative.
-     * @param startSpacing The spacing of the touch points at the beginning of the gesture. Must not
-     * be negative.
-     * @param endSpacing The spacing of the touch points at the end of the gesture. Must not be
-     * negative.
-     * @param orientation The angle, in degrees, of the gesture. 0 represents a horizontal pinch
-     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
-     *
-     * @return A description of a pinch centered at ({code centerX}, {@code centerY}) that starts
-     * with the touch points spaced by {@code startSpacing} and ends with them spaced by
-     * {@code endSpacing} that lasts {@code duration} ms. Returns {@code null} if either path
-     * specified for the pinch is invalid.
-     */
-    public static GestureDescription createPinch(@IntRange(from = 0) int centerX,
-            @IntRange(from = 0) int centerY,
-            @IntRange(from = 0) int startSpacing,
-            @IntRange(from = 0) int endSpacing,
-            float orientation,
-            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
-        if ((startSpacing < 0) || (endSpacing < 0)) {
-            throw new IllegalArgumentException("Pinch spacing cannot be negative");
-        }
-        float[] startPoint1 = new float[2];
-        float[] endPoint1 = new float[2];
-        float[] startPoint2 = new float[2];
-        float[] endPoint2 = new float[2];
-
-        /* Build points for a horizontal gesture centered at the origin */
-        startPoint1[0] = startSpacing / 2;
-        startPoint1[1] = 0;
-        endPoint1[0] = endSpacing / 2;
-        endPoint1[1] = 0;
-        startPoint2[0] = -startSpacing / 2;
-        startPoint2[1] = 0;
-        endPoint2[0] = -endSpacing / 2;
-        endPoint2[1] = 0;
-
-        /* Rotate and translate the points */
-        Matrix matrix = new Matrix();
-        matrix.setRotate(orientation);
-        matrix.postTranslate(centerX, centerY);
-        matrix.mapPoints(startPoint1);
-        matrix.mapPoints(endPoint1);
-        matrix.mapPoints(startPoint2);
-        matrix.mapPoints(endPoint2);
-
-        Path path1 = new Path();
-        path1.moveTo(startPoint1[0], startPoint1[1]);
-        path1.lineTo(endPoint1[0], endPoint1[1]);
-        Path path2 = new Path();
-        path2.moveTo(startPoint2[0], startPoint2[1]);
-        path2.lineTo(endPoint2[0], endPoint2[1]);
-
-        return new GestureDescription(Arrays.asList(
-                new StrokeDescription(path1, 0, duration),
-                new StrokeDescription(path2, 0, duration)));
+    public static long getMaxGestureDuration() {
+        return MAX_GESTURE_DURATION_MS;
     }
 
     private GestureDescription() {}
@@ -180,10 +77,6 @@
         mStrokes.addAll(strokes);
     }
 
-    private GestureDescription(StrokeDescription stroke) {
-        mStrokes.add(stroke);
-    }
-
     /**
      * Get the number of stroke in the gesture.
      *
@@ -278,21 +171,23 @@
          */
         public Builder addStroke(@NonNull StrokeDescription strokeDescription) {
             if (mStrokes.size() >= MAX_STROKE_COUNT) {
-                throw new RuntimeException("Attempting to add too many strokes to a gesture");
+                throw new IllegalStateException(
+                        "Attempting to add too many strokes to a gesture");
             }
 
             mStrokes.add(strokeDescription);
 
             if (getTotalDuration(mStrokes) > MAX_GESTURE_DURATION_MS) {
                 mStrokes.remove(strokeDescription);
-                throw new RuntimeException("Gesture would exceed maximum duration with new stroke");
+                throw new IllegalStateException(
+                        "Gesture would exceed maximum duration with new stroke");
             }
             return this;
         }
 
         public GestureDescription build() {
             if (mStrokes.size() == 0) {
-                throw new RuntimeException("Gestures must have at least one stroke");
+                throw new IllegalStateException("Gestures must have at least one stroke");
             }
             return new GestureDescription(mStrokes);
         }
@@ -317,8 +212,8 @@
          * Must not be negative.
          */
         public StrokeDescription(@NonNull Path path,
-                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long startTime,
-                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+                @IntRange(from = 0) long startTime,
+                @IntRange(from = 0) long duration) {
             if (duration <= 0) {
                 throw new IllegalArgumentException("Duration must be positive");
             }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 33fd1db..13e8e75 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -49,6 +50,8 @@
 import com.android.internal.content.ReferrerIntent;
 
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -78,7 +81,15 @@
     public static final String REPORT_KEY_STREAMRESULT = "stream";
 
     private static final String TAG = "Instrumentation";
-    
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES})
+    public @interface UiAutomationFlags {};
+
+
     private final Object mSync = new Object();
     private ActivityThread mThread = null;
     private MessageQueue mMessageQueue = null;
@@ -1876,7 +1887,7 @@
      *
      * @see UiAutomation
      */
-    public UiAutomation getUiAutomation(int flags) {
+    public UiAutomation getUiAutomation(@UiAutomationFlags int flags) {
         if (mUiAutomationConnection != null) {
             if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) {
                 mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(),
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ca19b9b..15cc17d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -691,6 +691,7 @@
      *
      * @see #getSharedPreferencesPath(String)
      * @see #MODE_PRIVATE
+     * @removed
      */
     public abstract SharedPreferences getSharedPreferences(File file, int mode);
 
@@ -813,17 +814,16 @@
      *            to get a path.
      * @return An absolute path to the given file.
      * @see #getSharedPreferences(String, int)
+     * @removed
      */
     public abstract File getSharedPreferencesPath(String name);
 
     /**
      * Returns the absolute path to the directory on the filesystem where all
-     * private files belonging to this app are stored. This is the top-level
-     * directory under which {@link #getFilesDir()}, {@link #getCacheDir()}, etc
-     * are contained. Apps should <em>not</em> create any files or directories
-     * as direct children of this directory, since it's a reserved namespace
-     * belonging to the platform. Instead, use {@link #getDir(String, int)} or
-     * other storage APIs.
+     * private files belonging to this app are stored. Apps should not use this
+     * path directly; they should instead use {@link #getFilesDir()},
+     * {@link #getCacheDir()}, {@link #getDir(String, int)}, or other storage
+     * APIs on this class.
      * <p>
      * The returned path may change over time if the calling app is moved to an
      * adopted storage device, so only relative paths should be persisted.
@@ -831,7 +831,7 @@
      * No additional permissions are required for the calling app to read or
      * write files under the returned path.
      *
-     * @see #getDir(String, int)
+     * @see ApplicationInfo#dataDir
      */
     public abstract File getDataDir();
 
@@ -4168,7 +4168,7 @@
      * Return a new Context object for the current Context but whose storage
      * APIs are backed by device-protected storage.
      * <p>
-     * When a device is encrypted, data stored in this location is encrypted
+     * On devices with direct boot, data stored in this location is encrypted
      * with a key tied to the physical device, and it can be accessed
      * immediately after the device has booted successfully, both
      * <em>before and after</em> the user has authenticated with their
@@ -4206,7 +4206,7 @@
      * storage area for apps unless
      * {@link android.R.attr#defaultToDeviceProtectedStorage} was requested.
      * <p>
-     * When a device is encrypted, data stored in this location is encrypted
+     * On devices with direct boot, data stored in this location is encrypted
      * with a key tied to user credentials, which can be accessed
      * <em>only after</em> the user has entered their credentials (such as a
      * lock pattern or PIN).
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 09aad3b..57ab7a2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -23,12 +23,14 @@
 import android.annotation.SystemApi;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -1946,31 +1948,48 @@
     public static final String ACTION_ALARM_CHANGED = "android.intent.action.ALARM_CHANGED";
 
     /**
-     * Broadcast Action: This is broadcast once, after the system has finished
-     * booting and the user is in a "locked" state. A user is locked when their
-     * credential-encrypted private app data storage is unavailable. Once the
-     * user has entered their credentials (such as a lock pattern or PIN) for
-     * the first time, the {@link #ACTION_BOOT_COMPLETED} broadcast will be
-     * sent.
+     * Broadcast Action: This is broadcast once, after the user has finished
+     * booting, but while still in the "locked" state. It can be used to perform
+     * application-specific initialization, such as installing alarms. You must
+     * hold the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}
+     * permission in order to receive this broadcast.
      * <p>
-     * You must hold the
-     * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission in
-     * order to receive this broadcast.
+     * This broadcast is sent immediately at boot by all devices (regardless of
+     * direct boot support) running {@link android.os.Build.VERSION_CODES#N} or
+     * higher. Upon receipt of this broadcast, the user is still locked and only
+     * device-protected storage can be accessed safely. If you want to access
+     * credential-protected storage, you need to wait for the user to be
+     * unlocked (typically by entering their lock pattern or PIN for the first
+     * time), after which the {@link #ACTION_USER_UNLOCKED} and
+     * {@link #ACTION_BOOT_COMPLETED} broadcasts are sent.
+     * <p>
+     * To receive this broadcast, your receiver component must be marked as
+     * being {@link ComponentInfo#directBootAware}.
      * <p class="note">
      * This is a protected intent that can only be sent by the system.
+     *
+     * @see Context#createDeviceProtectedStorageContext()
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";
 
     /**
-     * Broadcast Action: This is broadcast once, after the system has finished
-     * booting.  It can be used to perform application-specific initialization,
-     * such as installing alarms.  You must hold the
-     * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission
-     * in order to receive this broadcast.
-     *
-     * <p class="note">This is a protected intent that can only be sent
-     * by the system.
+     * Broadcast Action: This is broadcast once, after the user has finished
+     * booting. It can be used to perform application-specific initialization,
+     * such as installing alarms. You must hold the
+     * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission in
+     * order to receive this broadcast.
+     * <p>
+     * This broadcast is sent at boot by all devices (both with and without
+     * direct boot support). Upon receipt of this broadcast, the user is
+     * unlocked and both device-protected and credential-protected storage can
+     * accessed safely.
+     * <p>
+     * If you need to run while the user is still locked (before they've entered
+     * their lock pattern or PIN for the first time), you can listen for the
+     * {@link #ACTION_LOCKED_BOOT_COMPLETED} broadcast.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1079f97..e89cbd7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2593,12 +2593,15 @@
     public abstract Intent getLeanbackLaunchIntentForPackage(String packageName);
 
     /**
-     * Return an array of all of the secondary group-ids that have been assigned
-     * to a package.
+     * Return an array of all of the POSIX secondary group IDs that have been
+     * assigned to the given package.
+     * <p>
+     * Note that the same package may have different GIDs under different
+     * {@link UserHandle} on the same device.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
-     *         desired package.
-     * @return Returns an int array of the assigned gids, or null if there are
+     *            desired package.
+     * @return Returns an int array of the assigned GIDs, or null if there are
      *         none.
      * @throws NameNotFoundException if a package with the given name cannot be
      *             found on the system.
@@ -2607,8 +2610,11 @@
             throws NameNotFoundException;
 
     /**
-     * Return an array of all of the secondary group-ids that have been assigned
-     * to a package.
+     * Return an array of all of the POSIX secondary group IDs that have been
+     * assigned to the given package.
+     * <p>
+     * Note that the same package may have different GIDs under different
+     * {@link UserHandle} on the same device.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
      *            desired package.
@@ -2622,6 +2628,9 @@
 
     /**
      * Return the UID associated with the given package name.
+     * <p>
+     * Note that the same package will have different UIDs under different
+     * {@link UserHandle} on the same device.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
      *            desired package.
@@ -2634,6 +2643,9 @@
 
     /**
      * Return the UID associated with the given package name.
+     * <p>
+     * Note that the same package will have different UIDs under different
+     * {@link UserHandle} on the same device.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
      *            desired package.
@@ -2648,6 +2660,9 @@
 
     /**
      * Return the UID associated with the given package name.
+     * <p>
+     * Note that the same package will have different UIDs under different
+     * {@link UserHandle} on the same device.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
      *            desired package.
diff --git a/core/java/android/os/DeadSystemException.java b/core/java/android/os/DeadSystemException.java
index 595365c..8fb53e2 100644
--- a/core/java/android/os/DeadSystemException.java
+++ b/core/java/android/os/DeadSystemException.java
@@ -18,7 +18,7 @@
 
 /**
  * The core Android system has died and is going through a runtime restart. All
- * running apps will be promptly be killed.
+ * running apps will be promptly killed.
  */
 public class DeadSystemException extends DeadObjectException {
     public DeadSystemException() {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 307bd2d..f6e8940 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -34,6 +34,7 @@
     private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
     private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
     private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
+    private static final String ENV_ANDROID_EXPAND = "ANDROID_EXPAND";
     private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
     private static final String ENV_DOWNLOAD_CACHE = "DOWNLOAD_CACHE";
     private static final String ENV_OEM_ROOT = "OEM_ROOT";
@@ -54,6 +55,7 @@
 
     private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
     private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
+    private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand");
     private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
     private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache");
     private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
@@ -229,6 +231,11 @@
     }
 
     /** {@hide} */
+    public static File getExpandDirectory() {
+        return DIR_ANDROID_EXPAND;
+    }
+
+    /** {@hide} */
     public static File getDataSystemDirectory() {
         return new File(getDataDirectory(), "system");
     }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index dd10df3..1d464c0 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -50,6 +50,7 @@
     boolean isLightDeviceIdleMode();
 
     void reboot(boolean confirm, String reason, boolean wait);
+    void rebootSafeMode(boolean confirm, boolean wait);
     void shutdown(boolean confirm, String reason, boolean wait);
     void crash(String message);
 
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index cb4caea..8bc903b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -426,6 +426,12 @@
     public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";
 
     /**
+     * The 'reason' value used when rebooting in safe mode
+     * @hide
+     */
+    public static final String REBOOT_SAFE_MODE = "safemode";
+
+    /**
      * The value to pass as the 'reason' argument to android_reboot().
      * @hide
      */
@@ -903,6 +909,21 @@
     }
 
     /**
+     * Reboot the device. Will not return if the reboot is successful.
+     * <p>
+     * Requires the {@link android.Manifest.permission#REBOOT} permission.
+     * </p>
+     * @hide
+     */
+    public void rebootSafeMode() {
+        try {
+            mService.rebootSafeMode(false, true);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns true if the device is currently in power save mode.  When in this mode,
      * applications should reduce their functionality in order to conserve battery as
      * much as possible.  You can monitor for changes to this state with
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 92e1862..0ff0154 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -918,11 +918,15 @@
     }
 
     /**
-     * Return whether the calling user is running in an "unlocked" state. A user
-     * is unlocked only after they've entered their credentials (such as a lock
-     * pattern or PIN), and credential-encrypted private app data storage is
-     * available. When a user is locked, only device-protected data storage is
-     * available.
+     * Return whether the calling user is running in an "unlocked" state.
+     * <p>
+     * On devices with direct boot, a user is unlocked only after they've
+     * entered their credentials (such as a lock pattern or PIN). On devices
+     * without direct boot, a user is unlocked as soon as it starts.
+     * <p>
+     * When a user is locked, only device-protected data storage is available.
+     * When a user is unlocked, both device-protected and credential-protected
+     * private app data storage is available.
      *
      * @see Intent#ACTION_USER_UNLOCKED
      * @see Context#createDeviceProtectedStorageContext()
@@ -932,11 +936,15 @@
     }
 
     /**
-     * Return whether the given user is running in an "unlocked" state. A user
-     * is unlocked only after they've entered their credentials (such as a lock
-     * pattern or PIN), and credential-protected private app data storage is
-     * available. When a user is locked, only device-protected data storage is
-     * available.
+     * Return whether the given user is running in an "unlocked" state.
+     * <p>
+     * On devices with direct boot, a user is unlocked only after they've
+     * entered their credentials (such as a lock pattern or PIN). On devices
+     * without direct boot, a user is unlocked as soon as it starts.
+     * <p>
+     * When a user is locked, only device-protected data storage is available.
+     * When a user is unlocked, both device-protected and credential-protected
+     * private app data storage is available.
      *
      * @param user to retrieve the unlocked state for.
      * @see Intent#ACTION_USER_UNLOCKED
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 61e6b95..22aec63 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -35,6 +35,7 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.security.KeyStore;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
@@ -1025,6 +1026,20 @@
         }
     }
 
+    /**
+     * Return if data stored at the given path will be encrypted while at rest.
+     * This can help apps avoid the overhead of double-encrypting data.
+     */
+    public boolean isEncrypted(File file) {
+        if (FileUtils.contains(Environment.getDataDirectory(), file)) {
+            return isEncrypted();
+        } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) {
+            return true;
+        }
+        // TODO: extend to support shared storage
+        return false;
+    }
+
     /** {@hide}
      * Is this device encryptable or already encrypted?
      * @return true for encryptable or encrypted
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index c36680c..47dc6c3 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -362,8 +362,7 @@
      * Explicitly set the storage location used internally by this class to be
      * device-protected storage.
      * <p>
-     * <p>
-     * When a device is encrypted, data stored in this location is encrypted
+     * On devices with direct boot, data stored in this location is encrypted
      * with a key tied to the physical device, and it can be accessed
      * immediately after the device has booted successfully, both
      * <em>before and after</em> the user has authenticated with their
@@ -392,7 +391,7 @@
      * credential-protected storage. This is the default storage area for apps
      * unless {@code forceDeviceProtectedStorage} was requested.
      * <p>
-     * When a device is encrypted, data stored in this location is encrypted
+     * On devices with direct boot, data stored in this location is encrypted
      * with a key tied to user credentials, which can be accessed
      * <em>only after</em> the user has entered their credentials (such as a
      * lock pattern or PIN).
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index 5697dfc..9434f0c 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -54,6 +54,11 @@
     WebViewProviderInfo[] getValidWebViewPackages();
 
     /**
+     * Fetch all packages that could potentially implement WebView.
+     */
+    WebViewProviderInfo[] getAllWebViewPackages();
+
+    /**
      * Used by DevelopmentSetting to get the name of the WebView provider currently in use.
      */
     String getCurrentWebViewPackageName();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index c280b81..f1bf890 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -128,98 +128,9 @@
         public MissingWebViewPackageException(Exception e) { super(e); }
     }
 
-    private static String TAG_START = "webviewproviders";
-    private static String TAG_WEBVIEW_PROVIDER = "webviewprovider";
-    private static String TAG_PACKAGE_NAME = "packageName";
-    private static String TAG_DESCRIPTION = "description";
-    // Whether or not the provider must be explicitly chosen by the user to be used.
-    private static String TAG_AVAILABILITY = "availableByDefault";
-    private static String TAG_SIGNATURE = "signature";
-    private static String TAG_FALLBACK = "isFallback";
-
-    /**
-     * Reads all signatures at the current depth (within the current provider) from the XML parser.
-     */
-    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
-            XmlPullParserException {
-        List<String> signatures = new ArrayList<String>();
-        int outerDepth = parser.getDepth();
-        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
-            if (parser.getName().equals(TAG_SIGNATURE)) {
-                // Parse the value within the signature tag
-                String signature = parser.nextText();
-                signatures.add(signature);
-            } else {
-                Log.e(LOGTAG, "Found an element in a webview provider that is not a signature");
-            }
-        }
-        return signatures.toArray(new String[signatures.size()]);
-    }
-
-    /**
-     * Returns all packages declared in the framework resources as potential WebView providers.
-     * @hide
-     * */
-    public static WebViewProviderInfo[] getWebViewPackages() {
-        int numFallbackPackages = 0;
-        XmlResourceParser parser = null;
-        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
-        try {
-            parser = AppGlobals.getInitialApplication().getResources().getXml(
-                    com.android.internal.R.xml.config_webview_packages);
-            XmlUtils.beginDocument(parser, TAG_START);
-            while(true) {
-                XmlUtils.nextElement(parser);
-                String element = parser.getName();
-                if (element == null) {
-                    break;
-                }
-                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
-                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
-                    if (packageName == null) {
-                        throw new MissingWebViewPackageException(
-                                "WebView provider in framework resources missing package name");
-                    }
-                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
-                    if (description == null) {
-                        throw new MissingWebViewPackageException(
-                                "WebView provider in framework resources missing description");
-                    }
-                    boolean availableByDefault = "true".equals(
-                            parser.getAttributeValue(null, TAG_AVAILABILITY));
-                    boolean isFallback = "true".equals(
-                            parser.getAttributeValue(null, TAG_FALLBACK));
-                    WebViewProviderInfo currentProvider =
-                            new WebViewProviderInfo(packageName, description, availableByDefault,
-                                isFallback, readSignatures(parser));
-                    if (currentProvider.isFallbackPackage()) {
-                        numFallbackPackages++;
-                        if (numFallbackPackages > 1) {
-                            throw new AndroidRuntimeException(
-                                    "There can be at most one webview fallback package.");
-                        }
-                    }
-                    webViewProviders.add(currentProvider);
-                }
-                else {
-                    Log.e(LOGTAG, "Found an element that is not a webview provider");
-                }
-            }
-        } catch(XmlPullParserException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
-        } catch(IOException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
-    }
-
-
     // TODO (gsennton) remove when committing webview xts test change
     public static String getWebViewPackageName() {
-        WebViewProviderInfo[] providers = getWebViewPackages();
-        return providers[0].packageName;
+        return null;
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 64c2caa..75ccf35 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -150,6 +150,8 @@
     private WebViewProviderInfo(Parcel in) {
         packageName = in.readString();
         description = in.readString();
+        availableByDefault = (in.readInt() > 0);
+        isFallback = (in.readInt() > 0);
         signatures = in.createStringArray();
         packageInfo = null;
     }
@@ -163,6 +165,8 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(packageName);
         out.writeString(description);
+        out.writeInt(availableByDefault ? 1 : 0);
+        out.writeInt(isFallback ? 1 : 0);
         out.writeStringArray(signatures);
     }
 
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index e0ef86c..9e8f778 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -869,10 +869,10 @@
         // Either expand children with weight to take up available space or
         // shrink them if they extend beyond our current bounds. If we skipped
         // measurement on any children, we need to measure them now.
-        final int delta = heightSize - mTotalLength
+        int remainingExcess = heightSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
-        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
-            final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+            float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             mTotalLength = 0;
 
@@ -883,9 +883,12 @@
                 }
 
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final float childExtra = lp.weight;
-                if (childExtra > 0) {
-                    final int share = (int) (childExtra * delta / weightSum);
+                final float childWeight = lp.weight;
+                if (childWeight > 0) {
+                    final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
+                    remainingExcess -= share;
+                    remainingWeightSum -= childWeight;
+
                     final int childHeight;
                     if (lp.height == 0 && (!mAllowInconsistentMeasurement
                             || heightMode == MeasureSpec.EXACTLY)) {
@@ -1244,10 +1247,10 @@
         // Either expand children with weight to take up available space or
         // shrink them if they extend beyond our current bounds. If we skipped
         // measurement on any children, we need to measure them now.
-        final int delta = widthSize - mTotalLength
+        int remainingExcess = widthSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
-        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
-            final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+            float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
             maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
@@ -1262,9 +1265,12 @@
                 }
 
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final float childExtra = lp.weight;
-                if (childExtra > 0) {
-                    final int share = (int) (childExtra * delta / weightSum);
+                final float childWeight = lp.weight;
+                if (childWeight > 0) {
+                    final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
+                    remainingExcess -= share;
+                    remainingWeightSum -= childWeight;
+
                     final int childWidth;
                     if (lp.width == 0 && (!mAllowInconsistentMeasurement
                             || widthMode == MeasureSpec.EXACTLY)) {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index dcadb6a..36e0c77 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -879,12 +879,13 @@
     /**
      * Filter key down events. By forwarding key down events to this function,
      * views using non-modal ListPopupWindow can have it handle key selection of items.
-     *  
+     *
      * @param keyCode keyCode param passed to the host view's onKeyDown
      * @param event event param passed to the host view's onKeyDown
      * @return true if the event was handled, false if it was ignored.
-     * 
+     *
      * @see #setModal(boolean)
+     * @see #onKeyUp(int, KeyEvent)
      */
     public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
         // when the drop down is shown, we drive it directly
@@ -972,14 +973,15 @@
     }
 
     /**
-     * Filter key down events. By forwarding key up events to this function,
+     * Filter key up events. By forwarding key up events to this function,
      * views using non-modal ListPopupWindow can have it handle key selection of items.
-     *  
+     *
      * @param keyCode keyCode param passed to the host view's onKeyUp
      * @param event event param passed to the host view's onKeyUp
      * @return true if the event was handled, false if it was ignored.
-     * 
+     *
      * @see #setModal(boolean)
+     * @see #onKeyDown(int, KeyEvent)
      */
     public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
         if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
@@ -998,11 +1000,11 @@
      * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)}
      * events to this function, views using ListPopupWindow can have it dismiss the popup
      * when the back key is pressed.
-     *  
+     *
      * @param keyCode keyCode param passed to the host view's onKeyPreIme
      * @param event event param passed to the host view's onKeyPreIme
      * @return true if the event was handled, false if it was ignored.
-     * 
+     *
      * @see #setModal(boolean)
      */
     public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index dd6a41f..a1417f0 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,12 +16,10 @@
 
 package android.widget;
 
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
-
 import com.android.internal.R;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
@@ -54,10 +52,46 @@
 
 import java.lang.ref.WeakReference;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+
 /**
- * <p>A popup window that can be used to display an arbitrary view. The popup
- * window is a floating container that appears on top of the current
- * activity.</p>
+ * <p>
+ * This class represents a popup window that can be used to display an
+ * arbitrary view. The popup window is a floating container that appears on top
+ * of the current activity.
+ * </p>
+ * <a name="Animation"></a>
+ * <h3>Animation</h3>
+ * <p>
+ * On all versions of Android, popup window enter and exit animations may be
+ * specified by calling {@link #setAnimationStyle(int)} and passing the
+ * resource ID for an animation style that defines {@code windowEnterAnimation}
+ * and {@code windowExitAnimation}. For example, passing
+ * {@link android.R.style#Animation_Dialog} will give a scale and alpha
+ * animation.
+ * </br>
+ * A window animation style may also be specified in the popup window's style
+ * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle}
+ * attribute.
+ * </p>
+ * <p>
+ * Starting with API 23, more complex popup window enter and exit transitions
+ * may be specified by calling either {@link #setEnterTransition(Transition)}
+ * or {@link #setExitTransition(Transition)} and passing a  {@link Transition}.
+ * </br>
+ * Popup enter and exit transitions may also be specified in the popup window's
+ * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition}
+ * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition}
+ * attributes, respectively.
+ * </p>
+ *
+ * @attr ref android.R.styleable#PopupWindow_overlapAnchor
+ * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle
+ * @attr ref android.R.styleable#PopupWindow_popupBackground
+ * @attr ref android.R.styleable#PopupWindow_popupElevation
+ * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
+ * @attr ref android.R.styleable#PopupWindow_popupExitTransition
  *
  * @see android.widget.AutoCompleteTextView
  * @see android.widget.Spinner
@@ -351,15 +385,54 @@
         setFocusable(focusable);
     }
 
-    public void setEnterTransition(Transition enterTransition) {
+    /**
+     * Sets the enter transition to be used when the popup window is shown.
+     *
+     * @param enterTransition the enter transition, or {@code null} to clear
+     * @see #getEnterTransition()
+     * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
+     */
+    public void setEnterTransition(@Nullable Transition enterTransition) {
         mEnterTransition = enterTransition;
     }
 
-    public void setExitTransition(Transition exitTransition) {
+    /**
+     * Returns the enter transition to be used when the popup window is shown.
+     *
+     * @return the enter transition, or {@code null} if not set
+     * @see #setEnterTransition(Transition)
+     * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
+     */
+    @Nullable
+    public Transition getEnterTransition() {
+        return mEnterTransition;
+    }
+
+    /**
+     * Sets the exit transition to be used when the popup window is dismissed.
+     *
+     * @param exitTransition the exit transition, or {@code null} to clear
+     * @see #getExitTransition()
+     * @attr ref android.R.styleable#PopupWindow_popupExitTransition
+     */
+    public void setExitTransition(@Nullable Transition exitTransition) {
         mExitTransition = exitTransition;
     }
 
     /**
+     * Returns the exit transition to be used when the popup window is
+     * dismissed.
+     *
+     * @return the exit transition, or {@code null} if not set
+     * @see #setExitTransition(Transition)
+     * @attr ref android.R.styleable#PopupWindow_popupExitTransition
+     */
+    @Nullable
+    public Transition getExitTransition() {
+        return mExitTransition;
+    }
+
+    /**
      * Sets the bounds used as the epicenter of the enter and exit transitions.
      * <p>
      * Transitions use a point or Rect, referred to as the epicenter, to orient
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9897b12..80e1db0 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -128,8 +128,8 @@
                 com.android.internal.R.string.whichSendApplication,
                 com.android.internal.R.string.whichSendApplicationNamed),
         SENDTO(Intent.ACTION_SENDTO,
-                com.android.internal.R.string.whichSendApplication,
-                com.android.internal.R.string.whichSendApplicationNamed),
+                com.android.internal.R.string.whichSendToApplication,
+                com.android.internal.R.string.whichSendToApplicationNamed),
         SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
                 com.android.internal.R.string.whichSendApplication,
                 com.android.internal.R.string.whichSendApplicationNamed),
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 919254a..66cc975 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -149,6 +149,12 @@
     native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
+    /**
+     * Zygote unmount storage space on initializing.
+     * This method is called once.
+     */
+    native protected static void nativeUnmountStorageOnInit();
+
     private static void callPostForkChildHooks(int debugFlags, boolean isSystemServer,
             String instructionSet) {
         VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 3abea26..b658f87 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -643,6 +643,9 @@
             // Zygote.
             Trace.setTracingEnabled(false);
 
+            // Zygote process unmounts root storage spaces.
+            Zygote.nativeUnmountStorageOnInit();
+
             if (startSystemServer) {
                 startSystemServer(abiList, socketName);
             }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index ce03bb8..8682030 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -308,9 +308,6 @@
         private static final int MIN_OVERFLOW_SIZE = 2;
         private static final int MAX_OVERFLOW_SIZE = 4;
 
-        /* The duration of the overflow button vector animation duration. */
-        private static final int OVERFLOW_BUTTON_ANIMATION_DELAY = 400;
-
         private final Context mContext;
         private final View mParent;  // Parent for the popup window.
         private final PopupWindow mPopupWindow;
@@ -377,18 +374,6 @@
             }
         };
 
-        /* Runnable to reset the overflow button's drawable after an overflow transition. */
-        private final Runnable mResetOverflowButtonDrawable = new Runnable() {
-            @Override
-            public void run() {
-                if (mIsOverflowOpen) {
-                    mOverflowButton.setImageDrawable(mArrow);
-                } else {
-                    mOverflowButton.setImageDrawable(mOverflow);
-                }
-            }
-        };
-
         private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
         private boolean mHidden; // tracks whether this popup is hidden or hiding.
 
@@ -902,7 +887,9 @@
                 final Size containerSize = mOverflowPanelSize;
                 setSize(mContentContainer, containerSize);
                 mMainPanel.setAlpha(0);
+                mMainPanel.setVisibility(View.INVISIBLE);
                 mOverflowPanel.setAlpha(1);
+                mOverflowPanel.setVisibility(View.VISIBLE);
                 mOverflowButton.setImageDrawable(mArrow);
 
                 // Update x-coordinates depending on RTL state.
@@ -941,7 +928,9 @@
                 final Size containerSize = mMainPanelSize;
                 setSize(mContentContainer, containerSize);
                 mMainPanel.setAlpha(1);
+                mMainPanel.setVisibility(View.VISIBLE);
                 mOverflowPanel.setAlpha(0);
+                mOverflowPanel.setVisibility(View.INVISIBLE);
                 mOverflowButton.setImageDrawable(mOverflow);
 
                 if (hasOverflow()) {
@@ -1327,8 +1316,6 @@
                         mToArrow.start();
                         openOverflow();
                     }
-                    overflowButton.postDelayed(
-                            mResetOverflowButtonDrawable, OVERFLOW_BUTTON_ANIMATION_DELAY);
                 }
             });
             return overflowButton;
@@ -1389,6 +1376,10 @@
                     // Disable the overflow button while it's animating.
                     // It will be re-enabled when the animation stops.
                     mOverflowButton.setEnabled(false);
+                    // Ensure both panels have visibility turned on when the overflow animation
+                    // starts.
+                    mMainPanel.setVisibility(View.VISIBLE);
+                    mOverflowPanel.setVisibility(View.VISIBLE);
                 }
 
                 @Override
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 612f4df..3f4b2a6 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -302,9 +302,6 @@
         return false;
     }
 
-    // Unmount storage provided by root namespace and mount requested view
-    UnmountTree("/storage");
-
     String8 storageSource;
     if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
         storageSource = "/mnt/runtime/default";
@@ -667,12 +664,24 @@
   return pid;
 }
 
+static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
+    // Zygote process unmount root storage space initially before every child processes are forked.
+    // Every forked child processes (include SystemServer) only mount their own root storage space
+    // And no need unmount storage operation in MountEmulatedStorage method.
+    // Zygote process does not utilize root storage spaces and unshared its mount namespace from the ART.
+
+    UnmountTree("/storage");
+    return;
+}
+
 static const JNINativeMethod gMethods[] = {
     { "nativeForkAndSpecialize",
       "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
-      (void *) com_android_internal_os_Zygote_nativeForkSystemServer }
+      (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
+    { "nativeUnmountStorageOnInit", "()V",
+      (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/res/res/anim/progress_indeterminate_rotation_material.xml b/core/res/res/anim/progress_indeterminate_rotation_material.xml
index 5d3ba22..6e12105 100644
--- a/core/res/res/anim/progress_indeterminate_rotation_material.xml
+++ b/core/res/res/anim/progress_indeterminate_rotation_material.xml
@@ -16,7 +16,7 @@
 -->
 
 <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="6665"
+    android:duration="4444"
     android:interpolator="@android:anim/linear_interpolator"
     android:propertyName="rotation"
     android:repeatCount="-1"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
index cd678f1..6448f3b 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
@@ -16,22 +16,22 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="76dp"
         android:width="76dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48"
+        android:viewportHeight="76"
+        android:viewportWidth="76"
         android:tint="?attr/colorControlActivated">
 
     <group
         android:name="root"
-        android:translateX="24.0"
-        android:translateY="24.0" >
+        android:translateX="38.0"
+        android:translateY="38.0" >
         <path
             android:name="progressBar"
             android:fillColor="#00000000"
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -29 a 29,29 0 1,1 0,58 a 29,29 0 1,1 0,-58"
             android:strokeColor="@color/white"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
-            android:strokeWidth="4"
+            android:strokeWidth="6"
             android:trimPathEnd="0"
             android:trimPathOffset="0"
             android:trimPathStart="0" />
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
index 7f038f4..80e3355 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
@@ -27,7 +27,7 @@
         <path
             android:name="progressBar"
             android:fillColor="#00000000"
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -18 a 18,18 0 1,1 0,36 a 18,18 0 1,1 0,-36"
             android:strokeColor="@color/white"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
index 5625788..ebb632a 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
@@ -16,22 +16,22 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="16dp"
         android:width="16dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48"
+        android:viewportHeight="16"
+        android:viewportWidth="16"
         android:tint="?attr/colorControlActivated">
 
     <group
         android:name="root"
-        android:translateX="24.0"
-        android:translateY="24.0" >
+        android:translateX="8.0"
+        android:translateY="8.0" >
         <path
             android:name="progressBar"
             android:fillColor="#00000000"
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -5.9375 a 5.9375,5.9375 0 1,1 0,11.875 a 5.9375,5.9375 0 1,1 0,-11.875"
             android:strokeColor="@color/white"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
-            android:strokeWidth="4"
+            android:strokeWidth="2.125"
             android:trimPathEnd="0"
             android:trimPathOffset="0"
             android:trimPathStart="0" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3c5b4c0..7b85c7e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2707,6 +2707,8 @@
     <public type="attr" name="tunerCount" />
     <public type="attr" name="nfcAntennaPositionDrawable" />
     <public type="attr" name="fillType" />
+    <public type="attr" name="popupEnterTransition" />
+    <public type="attr" name="popupExitTransition" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
     <public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5d083d7..ba9bcaa 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2567,6 +2567,13 @@
     <!-- Title of intent resolver dialog when selecting a sharing application to run
          and a previously used application is known. -->
     <string name="whichSendApplicationNamed">Share with %1$s</string>
+    <!-- Title of intent resolver dialog when selecting an application to run to
+         send content to a specific recipient. Often used for email. -->
+    <string name="whichSendToApplication">Send using</string>
+    <!-- Title of intent resolver dialog when selecting an application to run to
+         send content to a specific recipient and a previously used application is known.
+         Often used for email. -->
+    <string name="whichSendToApplicationNamed">Send using %1$s</string>
     <!-- Title of intent resolver dialog when selecting a HOME application to run. -->
     <string name="whichHomeApplication">Select a Home app</string>
     <!-- Title of intent resolver dialog when selecting a HOME application to run
@@ -2936,8 +2943,8 @@
 
     <!-- Title of the pop-up dialog in which the user switches keyboard, also known as input method. -->
     <string name="select_input_method">Change keyboard</string>
-    <!-- Title of a button to open the settings to enable or disable keyboards, also known as input methods [CHAR LIMIT=30] -->
-    <string name="configure_input_methods">Choose keyboards</string>
+    <!-- Title of a button to open the settings to enable or disable other soft keyboards (also known as input methods) [CHAR LIMIT=30] -->
+    <string name="configure_input_methods">Other keyboards</string>
     <!-- Summary text of a toggle switch to enable/disable use of the IME while a physical
          keyboard is connected -->
     <string name="show_ime">Keep it on screen while physical keyboard is active</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f01cce3..8b78183 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2213,6 +2213,8 @@
   <java-symbol type="string" name="whichEditApplicationNamed" />
   <java-symbol type="string" name="whichSendApplication" />
   <java-symbol type="string" name="whichSendApplicationNamed" />
+  <java-symbol type="string" name="whichSendToApplication" />
+  <java-symbol type="string" name="whichSendToApplicationNamed" />
   <java-symbol type="attr" name="lightY" />
   <java-symbol type="attr" name="lightZ" />
   <java-symbol type="attr" name="lightRadius" />
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 7a6aad6..2d3935b 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -870,39 +870,56 @@
                 // AsyncCommand.AsyncCommandHandler#handleMessage
                 if (isFailed()) {
                     if (DEBUG) {
-                        Log.i(LOG_TAG, "[CALLBACK] on canceled layout command");
+                        Log.i(LOG_TAG, "[CALLBACK] on failed layout command");
                     }
 
                     return;
-                } else {
-                    if (message.what != MSG_ON_LAYOUT_STARTED) {
-                        // No need to force cancel anymore if layout finished
-                        removeForceCancel();
-                    }
                 }
 
-                switch (message.what) {
+                int sequence;
+                int what = message.what;
+                switch (what) {
+                    case MSG_ON_LAYOUT_FINISHED:
+                        removeForceCancel();
+                        sequence = message.arg2;
+                        break;
+                    case MSG_ON_LAYOUT_FAILED:
+                    case MSG_ON_LAYOUT_CANCELED:
+                        removeForceCancel();
+                        // $FALL-THROUGH - message uses the same format as "started"
+                    case MSG_ON_LAYOUT_STARTED:
+                        // Don't remote force-cancel as command is still running and might need to
+                        // be canceled later
+                        sequence = message.arg1;
+                        break;
+                    default:
+                        // not reached
+                        sequence = -1;
+                }
+
+                // If we are canceling any result is treated as a cancel
+                if (isCanceling() && what != MSG_ON_LAYOUT_STARTED) {
+                    what = MSG_ON_LAYOUT_CANCELED;
+                }
+
+                switch (what) {
                     case MSG_ON_LAYOUT_STARTED: {
                         ICancellationSignal cancellation = (ICancellationSignal) message.obj;
-                        final int sequence = message.arg1;
                         handleOnLayoutStarted(cancellation, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_FINISHED: {
                         PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
                         final boolean changed = (message.arg1 == 1);
-                        final int sequence = message.arg2;
                         handleOnLayoutFinished(info, changed, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_FAILED: {
                         CharSequence error = (CharSequence) message.obj;
-                        final int sequence = message.arg1;
                         handleOnLayoutFailed(error, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_CANCELED: {
-                        final int sequence = message.arg1;
                         handleOnLayoutCanceled(sequence);
                     } break;
                 }
@@ -1142,18 +1159,30 @@
                 // AsyncCommand.AsyncCommandHandler#handleMessage
                 if (isFailed()) {
                     if (DEBUG) {
-                        Log.i(LOG_TAG, "[CALLBACK] on canceled write command");
+                        Log.i(LOG_TAG, "[CALLBACK] on failed write command");
                     }
 
                     return;
-                } else {
-                    if (message.what != MSG_ON_WRITE_STARTED) {
-                        // No need to force cancel anymore if write finished
-                        removeForceCancel();
-                    }
                 }
 
-                switch (message.what) {
+                int what = message.what;
+                switch (what) {
+                    case MSG_ON_WRITE_FINISHED:
+                    case MSG_ON_WRITE_FAILED:
+                    case MSG_ON_WRITE_CANCELED:
+                        removeForceCancel();
+                    case MSG_ON_WRITE_STARTED:
+                        // Don't remote force-cancel as command is still running and might need to
+                        // be canceled later
+                        break;
+                }
+
+                // If we are canceling any result is treated as a cancel
+                if (isCanceling() && what != MSG_ON_WRITE_STARTED) {
+                    what = MSG_ON_WRITE_CANCELED;
+                }
+
+                switch (what) {
                     case MSG_ON_WRITE_STARTED: {
                         ICancellationSignal cancellation = (ICancellationSignal) message.obj;
                         final int sequence = message.arg1;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index ad4823e..4e1180d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -159,6 +159,7 @@
     private static final int STATE_PRINTER_UNAVAILABLE = 6;
     private static final int STATE_UPDATE_SLOW = 7;
     private static final int STATE_PRINT_COMPLETED = 8;
+    private static final int STATE_FINISHING = 9;
 
     private static final int UI_STATE_PREVIEW = 0;
     private static final int UI_STATE_ERROR = 1;
@@ -2029,11 +2030,17 @@
     }
 
     private void doFinish() {
-        if (mPrintedDocument.isUpdating()) {
+        if (mPrintedDocument != null && mPrintedDocument.isUpdating()) {
             // The printedDocument will call doFinish() when the current command finishes
             return;
         }
 
+        if (mState == STATE_FINISHING) {
+            return;
+        }
+
+        mState = STATE_FINISHING;
+
         if (mPrinterRegistry != null) {
             mPrinterRegistry.setTrackedPrinter(null);
         }
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 5a6553f..9c2c0ab 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -27,29 +27,19 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:paddingEnd="12dp"
-            android:background="@android:color/white"
-            android:textColor="#D9000000"
+            android:textColor="@color/ksh_keyword_color"
             android:textSize="16sp"
             android:maxLines="5"
             android:singleLine="false"
             android:scrollHorizontally="false"
-            android:layout_alignParentStart="true"
-            android:minWidth="100dp"
-            android:maxWidth="260dp"/>
-    <!--TODO: introduce and use a layout that allows wrapping and right align -->
-    <LinearLayout
+            android:layout_alignParentStart="true"/>
+    <com.android.systemui.statusbar.KeyboardShortcutKeysLayout
             android:id="@+id/keyboard_shortcuts_item_container"
             android:layout_toEndOf="@+id/keyboard_shortcuts_keyword"
             android:orientation="horizontal"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:background="@android:color/white"
             android:layout_alignParentEnd="true"
-            android:gravity="end"
             android:textSize="14sp"
-            android:paddingStart="0dp"
-            android:paddingEnd="0dp"
-            android:scrollHorizontally="false"
-            android:minWidth="100dp"
-            android:maxWidth="260dp"/>
+            android:scrollHorizontally="false"/>
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 80a478a..6cb8470 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -22,4 +22,4 @@
           android:paddingStart="24dp"
           android:paddingTop="20dp"
           android:paddingEnd="24dp"
-          android:paddingBottom="13dp" />
+          android:paddingBottom="13dp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
index f73ee15..7aba1cf 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -17,7 +17,7 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="488dp"
+    android:layout_width="@dimen/ksh_layout_width"
     android:layout_height="wrap_content">
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index a2fa3b9..66963c4 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -104,4 +104,7 @@
 
     <!-- The side padding for the task stack. -->
     <dimen name="recents_stack_left_right_padding">64dp</dimen>
+
+    <!-- Keyboard shortcuts helper -->
+    <dimen name="ksh_layout_width">488dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index eeed0cf..a3f8b85 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -167,6 +167,7 @@
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_system_group_color">#ff00bcd4</color>
     <color name="ksh_application_group_color">#fff44336</color>
+    <color name="ksh_keyword_color">#d9000000</color>
 
     <!-- Background color of edit overflow -->
     <color name="qs_edit_overflow_bg">#455A64</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6a9f456..8b433f9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -639,4 +639,7 @@
 
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
+
+    <!-- Keyboard shortcuts helper -->
+    <dimen name="ksh_layout_width">@dimen/match_parent</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index f33ef65..aab45b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -749,7 +749,7 @@
     public final void onBusEvent(AllTaskViewsDismissedEvent event) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         if (ssp.hasDockedTask()) {
-            mRecentsView.showEmptyView(R.string.recents_empty_message_dismissed_all);
+            mRecentsView.showEmptyView(event.msgResId);
         } else {
             // Just go straight home (no animation necessary because there are no more task views)
             dismissRecentsToHome(false /* animateTaskViews */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
index cf74519..0352161 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
@@ -22,5 +22,10 @@
  * This is sent whenever all the task views in a stack have been dismissed.
  */
 public class AllTaskViewsDismissedEvent extends EventBus.Event {
-    // Simple event
+
+    public final int msgResId;
+
+    public AllTaskViewsDismissedEvent(int msgResId) {
+        this.msgResId = msgResId;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 5eeda72..16385c9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -245,7 +245,7 @@
             if (row.getViewType() == TASK_ROW_VIEW_TYPE) {
                 TaskRow taskRow = (TaskRow) row;
                 Task task = taskRow.task;
-                mStack.removeTask(task, AnimationProps.IMMEDIATE);
+                mStack.removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
                 EventBus.getDefault().send(new DeleteTaskDataEvent(task));
                 i = removeTaskRow(i);
             }
@@ -326,7 +326,7 @@
     public void onTaskRemoved(Task task, int position) {
         // Since this is removed from the history, we need to update the stack as well to ensure
         // that the model is correct. Since the stack is hidden, we can update it immediately.
-        mStack.removeTask(task, AnimationProps.IMMEDIATE);
+        mStack.removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
         removeTaskRow(position);
         if (mRows.isEmpty()) {
             dismissHistory();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 4d1c552..193bfff 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -219,7 +219,7 @@
          * Notifies when a task has been removed from the stack.
          */
         void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
-            Task newFrontMostTask, AnimationProps animation);
+            Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture);
 
         /**
          * Notifies when a task has been removed from the history.
@@ -513,14 +513,15 @@
      * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
      * how they should update themselves.
      */
-    public void removeTask(Task t, AnimationProps animation) {
+    public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
         if (mStackTaskList.contains(t)) {
             boolean wasFrontMostTask = (getStackFrontMostTask(false /* includeFreeform */) == t);
             removeTaskImpl(mStackTaskList, t);
             Task newFrontMostTask = getStackFrontMostTask(false  /* includeFreeform */);
             if (mCb != null) {
                 // Notify that a task has been removed
-                mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask, animation);
+                mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask, animation,
+                        fromDockGesture);
             }
         } else if (mHistoryTaskList.contains(t)) {
             removeTaskImpl(mHistoryTaskList, t);
@@ -558,9 +559,9 @@
                 if (notifyStackChanges) {
                     // If we are notifying, then remove the task now, otherwise the raw task list
                     // will be reset at the end of this method
-                    removeTask(task, AnimationProps.IMMEDIATE);
+                    removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
                     mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null,
-                            AnimationProps.IMMEDIATE);
+                            AnimationProps.IMMEDIATE, false /* fromDockGesture */);
                 }
             }
             task.setGroup(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 4458639..cf8c9bb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -135,7 +135,7 @@
 
     @Override
     public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
-            Task newFrontMostTask, AnimationProps animation) {
+            Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) {
         getAdapter().notifyItemRemoved(stack.getStackTasks().indexOf(removedTask));
         if (mFocusedTask == removedTask) {
             resetFocusedTask(removedTask);
@@ -144,7 +144,9 @@
         if (mStack.getStackTaskCount() == 0) {
             boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0);
             if (shouldFinishActivity) {
-                EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
+                EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
+                        ? R.string.recents_empty_message
+                        : R.string.recents_empty_message_dismissed_all));
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 9235673..db97e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -632,7 +632,8 @@
                                     AnimationProps stackAnim = new AnimationProps(
                                             TaskStackView.DEFAULT_SYNC_STACK_DURATION,
                                             Interpolators.FAST_OUT_SLOW_IN);
-                                    mTaskStackView.getStack().removeTask(event.task, stackAnim);
+                                    mTaskStackView.getStack().removeTask(event.task, stackAnim,
+                                            true /* fromDockGesture */);
                                 }
                             }));
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 84590f2..33d5bb7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -150,8 +150,7 @@
         mTaskView.setTranslationY(y);
 
         mVisibleDockStates.clear();
-        if (ActivityManager.supportsMultiWindow() &&
-                !ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) {
+        if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()) {
             if (!event.task.isDockable) {
                 Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message,
                         Toast.LENGTH_SHORT).show();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 93b5b6c..0b20d21 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1395,7 +1395,7 @@
      */
     @Override
     public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
-            Task newFrontMostTask, AnimationProps animation) {
+            Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) {
         if (mFocusedTask == removedTask) {
             resetFocusedTask(removedTask);
         }
@@ -1426,7 +1426,9 @@
 
         // If there are no remaining tasks, then just close recents
         if (mStack.getTaskCount() == 0) {
-            EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
+            EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
+                    ? R.string.recents_empty_message
+                    : R.string.recents_empty_message_dismissed_all));
         }
     }
 
@@ -1599,7 +1601,7 @@
                     tv.dismissTask();
                 } else {
                     // Otherwise, remove the task from the stack immediately
-                    mStack.removeTask(t, AnimationProps.IMMEDIATE);
+                    mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
                 }
             }
         }
@@ -1937,7 +1939,7 @@
 
         // Remove the task from the stack
         mStack.removeTask(task, new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
-                Interpolators.FAST_OUT_SLOW_IN));
+                Interpolators.FAST_OUT_SLOW_IN), false /* fromDockGesture */);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
new file mode 100644
index 0000000..ba3e774
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Layout used as a container for keyboard shortcut keys. It's children are wrapped and right
+ * aligned.
+ */
+public final class KeyboardShortcutKeysLayout extends ViewGroup {
+    private int mLineHeight;
+
+    public KeyboardShortcutKeysLayout(Context context) {
+        super(context);
+    }
+
+    public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
+        int childCount = getChildCount();
+        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
+        int lineHeight = 0;
+        int xPos = getPaddingLeft();
+        int yPos = getPaddingTop();
+
+        int childHeightMeasureSpec;
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+        } else {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        }
+
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
+                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                        childHeightMeasureSpec);
+                int childWidth = child.getMeasuredWidth();
+                lineHeight = Math.max(lineHeight,
+                        child.getMeasuredHeight() + layoutParams.mVerticalSpacing);
+
+                if (xPos + childWidth > width) {
+                    xPos = getPaddingLeft();
+                    yPos += lineHeight;
+                }
+                xPos += childWidth + layoutParams.mHorizontalSpacing;
+            }
+        }
+        this.mLineHeight = lineHeight;
+
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
+            height = yPos + lineHeight;
+        } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+            if (yPos + lineHeight < height) {
+                height = yPos + lineHeight;
+            }
+        }
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        int spacing = getHorizontalVerticalSpacing();
+        return new LayoutParams(spacing, spacing);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) {
+        int spacing = getHorizontalVerticalSpacing();
+        return new LayoutParams(spacing, spacing, layoutParams);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return (p instanceof LayoutParams);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int childCount = getChildCount();
+        int fullRowWidth = r - l;
+        int xPos = getPaddingLeft();
+        int yPos = getPaddingTop();
+        int lastHorizontalSpacing = 0;
+        // The index of the child which starts the current row.
+        int rowStartIdx = 0;
+
+        // Go through all the children.
+        for (int i = 0; i < childCount; i++) {
+            View currentChild = getChildAt(i);
+            if (currentChild.getVisibility() != GONE) {
+                int currentChildWidth = currentChild.getMeasuredWidth();
+                LayoutParams lp = (LayoutParams) currentChild.getLayoutParams();
+
+                // If the current child does not fit on this row.
+                if (xPos + currentChildWidth > fullRowWidth) {
+                    // Layout all the children on this row but the current one.
+                    layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos,
+                            lastHorizontalSpacing);
+                    // Update the positions for starting on the new row.
+                    xPos = getPaddingLeft();
+                    yPos += mLineHeight;
+                    rowStartIdx = i;
+                }
+
+                xPos += currentChildWidth + lp.mHorizontalSpacing;
+                lastHorizontalSpacing = lp.mHorizontalSpacing;
+            }
+        }
+
+        // Lay out the children on the last row.
+        if (rowStartIdx < childCount) {
+            layoutChildrenOnRow(rowStartIdx, childCount, fullRowWidth, xPos, yPos,
+                    lastHorizontalSpacing);
+        }
+    }
+
+    private int getHorizontalVerticalSpacing() {
+        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        return (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics);
+    }
+
+    private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos,
+            int yPos, int lastHorizontalSpacing) {
+        int freeSpace = fullRowWidth - xPos + lastHorizontalSpacing;
+        xPos = getPaddingLeft() + freeSpace;
+
+        for (int j = startIndex; j < endIndex; ++j) {
+            View currentChild = getChildAt(j);
+            currentChild.layout(
+                    xPos,
+                    yPos,
+                    xPos + currentChild.getMeasuredWidth(),
+                    yPos + currentChild.getMeasuredHeight());
+            xPos += currentChild.getMeasuredWidth()
+                    + ((LayoutParams) currentChild.getLayoutParams()).mHorizontalSpacing;
+        }
+    }
+
+    public static class LayoutParams extends ViewGroup.LayoutParams {
+        public final int mHorizontalSpacing;
+        public final int mVerticalSpacing;
+
+        public LayoutParams(int horizontalSpacing, int verticalSpacing,
+                ViewGroup.LayoutParams viewGroupLayout) {
+            super(viewGroupLayout);
+            this.mHorizontalSpacing = horizontalSpacing;
+            this.mVerticalSpacing = verticalSpacing;
+        }
+
+        public LayoutParams(int mHorizontalSpacing, int verticalSpacing) {
+            super(0, 0);
+            this.mHorizontalSpacing = mHorizontalSpacing;
+            this.mVerticalSpacing = verticalSpacing;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 0b7bfa8..60c2fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -29,6 +29,7 @@
 import android.view.KeyboardShortcutInfo;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.widget.LinearLayout;
@@ -153,7 +154,7 @@
                         .findViewById(R.id.keyboard_shortcuts_keyword);
                 textView.setText(info.getLabel());
 
-                LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView
+                ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_item_container);
                 List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
                 final int shortcutKeysSize = shortcutKeys.size();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 575de16..d526ef5 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1852,6 +1852,12 @@
         }
 
         private boolean checkPolicyAccess(String pkg) {
+            if (mAudioManagerInternal != null) {
+                final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
+                if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
+                    return true;
+                }
+            }
             return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
         }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 91d8671..cbd77d4 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.power;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -74,6 +75,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -162,6 +165,14 @@
     // Default setting for double tap to wake.
     private static final int DEFAULT_DOUBLE_TAP_TO_WAKE = 0;
 
+    /** Constants for {@link #shutdownOrRebootInternal} */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({HALT_MODE_SHUTDOWN, HALT_MODE_REBOOT, HALT_MODE_REBOOT_SAFE_MODE})
+    public @interface HaltMode {}
+    private static final int HALT_MODE_SHUTDOWN = 0;
+    private static final int HALT_MODE_REBOOT = 1;
+    private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
+
     private final Context mContext;
     private final ServiceThread mHandlerThread;
     private final PowerManagerHandler mHandler;
@@ -2392,7 +2403,7 @@
         updatePowerStateLocked();
     }
 
-    private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
+    private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
             final String reason, boolean wait) {
         if (mHandler == null || !mSystemReady) {
             throw new IllegalStateException("Too early to call shutdown() or reboot()");
@@ -2402,10 +2413,12 @@
             @Override
             public void run() {
                 synchronized (this) {
-                    if (shutdown) {
-                        ShutdownThread.shutdown(mContext, reason, confirm);
-                    } else {
+                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
+                        ShutdownThread.rebootSafeMode(mContext, confirm);
+                    } else if (haltMode == HALT_MODE_REBOOT) {
                         ShutdownThread.reboot(mContext, reason, confirm);
+                    } else {
+                        ShutdownThread.shutdown(mContext, reason, confirm);
                     }
                 }
             }
@@ -3465,7 +3478,26 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                shutdownOrRebootInternal(false, confirm, reason, wait);
+                shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        /**
+         * Reboots the device into safe mode
+         *
+         * @param confirm If true, shows a reboot confirmation dialog.
+         * @param wait If true, this call waits for the reboot to complete and does not return.
+         */
+        @Override // Binder call
+        public void rebootSafeMode(boolean confirm, boolean wait) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm,
+                        PowerManager.REBOOT_SAFE_MODE, wait);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3483,7 +3515,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                shutdownOrRebootInternal(true, confirm, reason, wait);
+                shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index a4bbb51..50699f8 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -35,14 +35,14 @@
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.provider.Settings;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
+import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
-import android.webkit.WebViewFactory;
 
 import com.android.server.SystemService;
 
@@ -76,9 +76,11 @@
     private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
+    private WebViewUtilityInterface mWebViewUtility;
 
     public WebViewUpdateService(Context context) {
         super(context);
+        mWebViewUtility = new WebViewUtilityImpl();
     }
 
     @Override
@@ -114,7 +116,7 @@
 
                     updateFallbackState(context, intent);
 
-                    for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
+                    for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) {
                         String webviewPackage = "package:" + provider.packageName;
 
                         if (webviewPackage.equals(intent.getDataString())) {
@@ -153,11 +155,7 @@
                                 // package that was not the previous provider then we must kill
                                 // packages dependent on the old package ourselves. The framework
                                 // only kills dependents of packages that are being removed.
-                                try {
-                                    ActivityManagerNative.getDefault().killPackageDependents(
-                                        oldProviderName, UserHandle.USER_ALL);
-                                } catch (RemoteException e) {
-                                }
+                                mWebViewUtility.killPackageDependents(oldProviderName);
                             }
                             return;
                         }
@@ -170,7 +168,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         // Make sure we only receive intents for WebView packages from our config file.
-        for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
+        for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) {
             filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
         }
         getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
@@ -210,7 +208,7 @@
     void handleNewUser(int userId) {
         if (!isFallbackLogicEnabled()) return;
 
-        WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
+        WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
         if (fallbackProvider == null) return;
         boolean existsValidNonFallbackProvider =
@@ -228,7 +226,7 @@
     void updateFallbackState(final Context context, final Intent intent) {
         if (!isFallbackLogicEnabled()) return;
 
-        WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
+        WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
 
         if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
                     || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) {
@@ -319,10 +317,10 @@
         return false;
     }
 
-    private static boolean isFallbackPackage(String packageName) {
+    private boolean isFallbackPackage(String packageName) {
         if (packageName == null || !isFallbackLogicEnabled()) return false;
 
-        WebViewProviderInfo[] webviewPackages = WebViewFactory.getWebViewPackages();
+        WebViewProviderInfo[] webviewPackages = mWebViewUtility.getWebViewPackages();
         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
         return (fallbackProvider != null
                 && packageName.equals(fallbackProvider.packageName));
@@ -359,13 +357,13 @@
         PackageInfo newPackage = null;
         synchronized(this) {
             oldPackage = mCurrentWebViewPackage;
-            updateUserSetting(newProviderName);
+            mWebViewUtility.updateUserSetting(getContext(), newProviderName);
 
             try {
                 newPackage = findPreferredWebViewPackage();
                 if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
                     // If we don't perform the user change, revert the settings change.
-                    updateUserSetting(newPackage.packageName);
+                    mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName);
                     return newPackage.packageName;
                 }
             } catch (WebViewFactory.MissingWebViewPackageException e) {
@@ -378,12 +376,8 @@
             onWebViewProviderChanged(newPackage);
         }
         // Kill apps using the old provider
-        try {
-            if (oldPackage != null) {
-                ActivityManagerNative.getDefault().killPackageDependents(
-                        oldPackage.packageName, UserHandle.USER_ALL);
-            }
-        } catch (RemoteException e) {
+        if (oldPackage != null) {
+            mWebViewUtility.killPackageDependents(oldPackage.packageName);
         }
         return newPackage.packageName;
     }
@@ -397,14 +391,14 @@
             mAnyWebViewInstalled = true;
             if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
                 mCurrentWebViewPackage = newPackage;
-                updateUserSetting(newPackage.packageName);
+                mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName);
 
                 // The relro creations might 'finish' (not start at all) before
                 // WebViewFactory.onWebViewProviderChanged which means we might not know the number
                 // of started creations before they finish.
                 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
                 mNumRelroCreationsFinished = 0;
-                mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage);
+                mNumRelroCreationsStarted = mWebViewUtility.onWebViewProviderChanged(newPackage);
                 // If the relro creations finish before we know the number of started creations we
                 // will have to do any cleanup/notifying here.
                 checkIfRelrosDoneLocked();
@@ -421,7 +415,7 @@
      * */
     private void updateValidWebViewPackages() {
         List<WebViewProviderInfo> webViewProviders  =
-            new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages()));
+            new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages()));
         Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
         // remove non-valid packages
         while(it.hasNext()) {
@@ -435,17 +429,6 @@
         }
     }
 
-    private static String getUserChosenWebViewProvider() {
-        return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
-                Settings.Global.WEBVIEW_PROVIDER);
-    }
-
-    private void updateUserSetting(String newProviderName) {
-        Settings.Global.putString(getContext().getContentResolver(),
-                Settings.Global.WEBVIEW_PROVIDER,
-                newProviderName == null ? "" : newProviderName);
-    }
-
     /**
      * Returns either the package info of the WebView provider determined in the following way:
      * If the user has chosen a provider then use that if it is valid,
@@ -456,7 +439,7 @@
     private PackageInfo findPreferredWebViewPackage() {
         WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
 
-        String userChosenProvider = getUserChosenWebViewProvider();
+        String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext());
 
         // If the user has chosen provider, use that
         for (WebViewProviderInfo provider : providers) {
@@ -616,6 +599,11 @@
         }
 
         @Override // Binder call
+        public WebViewProviderInfo[] getAllWebViewPackages() {
+            return WebViewUpdateService.this.mWebViewUtility.getWebViewPackages();
+        }
+
+        @Override // Binder call
         public String getCurrentWebViewPackageName() {
             synchronized(WebViewUpdateService.this) {
                 if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
@@ -626,7 +614,7 @@
 
         @Override // Binder call
         public boolean isFallbackPackage(String packageName) {
-            return WebViewUpdateService.isFallbackPackage(packageName);
+            return WebViewUpdateService.this.isFallbackPackage(packageName);
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
new file mode 100644
index 0000000..4dbd02d
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 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.server.webkit;
+
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactory.MissingWebViewPackageException;
+import android.webkit.WebViewProviderInfo;
+
+import com.android.internal.util.XmlUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Default implementation for the WebView preparation Utility interface.
+ * @hide
+ */
+public class WebViewUtilityImpl implements WebViewUtilityInterface {
+    private static final String TAG = WebViewUtilityImpl.class.getSimpleName();
+    private static final String TAG_START = "webviewproviders";
+    private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider";
+    private static final String TAG_PACKAGE_NAME = "packageName";
+    private static final String TAG_DESCRIPTION = "description";
+    // Whether or not the provider must be explicitly chosen by the user to be used.
+    private static final String TAG_AVAILABILITY = "availableByDefault";
+    private static final String TAG_SIGNATURE = "signature";
+    private static final String TAG_FALLBACK = "isFallback";
+
+    /**
+     * Returns all packages declared in the framework resources as potential WebView providers.
+     * @hide
+     * */
+    @Override
+    public WebViewProviderInfo[] getWebViewPackages() {
+        int numFallbackPackages = 0;
+        XmlResourceParser parser = null;
+        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
+        try {
+            parser = AppGlobals.getInitialApplication().getResources().getXml(
+                    com.android.internal.R.xml.config_webview_packages);
+            XmlUtils.beginDocument(parser, TAG_START);
+            while(true) {
+                XmlUtils.nextElement(parser);
+                String element = parser.getName();
+                if (element == null) {
+                    break;
+                }
+                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
+                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
+                    if (packageName == null) {
+                        throw new MissingWebViewPackageException(
+                                "WebView provider in framework resources missing package name");
+                    }
+                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
+                    if (description == null) {
+                        throw new MissingWebViewPackageException(
+                                "WebView provider in framework resources missing description");
+                    }
+                    boolean availableByDefault = "true".equals(
+                            parser.getAttributeValue(null, TAG_AVAILABILITY));
+                    boolean isFallback = "true".equals(
+                            parser.getAttributeValue(null, TAG_FALLBACK));
+                    WebViewProviderInfo currentProvider =
+                            new WebViewProviderInfo(packageName, description, availableByDefault,
+                                isFallback, readSignatures(parser));
+                    if (currentProvider.isFallbackPackage()) {
+                        numFallbackPackages++;
+                        if (numFallbackPackages > 1) {
+                            throw new AndroidRuntimeException(
+                                    "There can be at most one webview fallback package.");
+                        }
+                    }
+                    webViewProviders.add(currentProvider);
+                }
+                else {
+                    Log.e(TAG, "Found an element that is not a webview provider");
+                }
+            }
+        } catch(XmlPullParserException e) {
+            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+        } catch(IOException e) {
+            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
+    }
+
+    /**
+     * Reads all signatures at the current depth (within the current provider) from the XML parser.
+     */
+    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
+            XmlPullParserException {
+        List<String> signatures = new ArrayList<String>();
+        int outerDepth = parser.getDepth();
+        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals(TAG_SIGNATURE)) {
+                // Parse the value within the signature tag
+                String signature = parser.nextText();
+                signatures.add(signature);
+            } else {
+                Log.e(TAG, "Found an element in a webview provider that is not a signature");
+            }
+        }
+        return signatures.toArray(new String[signatures.size()]);
+    }
+
+    @Override
+    public int onWebViewProviderChanged(PackageInfo packageInfo) {
+        return WebViewFactory.onWebViewProviderChanged(packageInfo);
+    }
+
+    @Override
+    public String getUserChosenWebViewProvider(Context context) {
+        return Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.WEBVIEW_PROVIDER);
+    }
+
+    @Override
+    public void updateUserSetting(Context context, String newProviderName) {
+        Settings.Global.putString(context.getContentResolver(),
+                Settings.Global.WEBVIEW_PROVIDER,
+                newProviderName == null ? "" : newProviderName);
+    }
+
+    @Override
+    public void killPackageDependents(String packageName) {
+        try {
+            ActivityManagerNative.getDefault().killPackageDependents(packageName,
+                    UserHandle.USER_ALL);
+        } catch (RemoteException e) {
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java
new file mode 100644
index 0000000..1919f40
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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.server.webkit;
+
+import android.webkit.WebViewProviderInfo;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+
+/**
+ * Utility interface for the WebViewUpdateService.
+ * This interface provides a way to test the WebView preparation mechanism - during normal use this
+ * interface is implemented using calls to the Android framework, but by providing an alternative
+ * implementation we can test the WebView preparation logic without reaching other framework code.
+ * @hide
+ */
+public interface WebViewUtilityInterface {
+    public WebViewProviderInfo[] getWebViewPackages();
+    public int onWebViewProviderChanged(PackageInfo packageInfo);
+
+    public String getUserChosenWebViewProvider(Context context);
+    public void updateUserSetting(Context context, String newProviderName);
+    public void killPackageDependents(String packageName);
+}
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index f2f555b..8ab1878 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -443,10 +443,7 @@
             @Nullable List<PrinterId> printerIds) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
-            // No services - nothing to do.
-            if (mActiveServices.isEmpty()) {
-                return;
-            }
+
             // No session - nothing to do.
             if (mPrinterDiscoverySession == null) {
                 return;
@@ -460,10 +457,7 @@
     public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
-            // No services - nothing to do.
-            if (mActiveServices.isEmpty()) {
-                return;
-            }
+
             // No session - nothing to do.
             if (mPrinterDiscoverySession == null) {
                 return;