Merge "Handle null package argument"
diff --git a/Android.mk b/Android.mk
index 64a8b89..d7e8e4e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -209,6 +209,7 @@
 	core/java/android/security/IKeystoreService.aidl \
 	core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
 	core/java/android/service/carrier/ICarrierMessagingService.aidl \
+	core/java/android/service/gatekeeper/IGateKeeperService.aidl \
 	core/java/android/service/notification/INotificationListener.aidl \
 	core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
 	core/java/android/service/notification/IConditionListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index ea4aaf9..fdeda0b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3555,6 +3555,7 @@
     method public int getMemoryClass();
     method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
     method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
+    method public int getPackageImportance(java.lang.String);
     method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
     method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
     method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
@@ -3647,9 +3648,11 @@
     field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
     field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
     field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
+    field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
     field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
     field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
     field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
+    field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
     field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
     field public static final int REASON_PROVIDER_IN_USE = 1; // 0x1
     field public static final int REASON_SERVICE_IN_USE = 2; // 0x2
@@ -13048,6 +13051,8 @@
     method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
     method public abstract android.hardware.camera2.CameraDevice getDevice();
+    method public abstract android.view.Surface getInputSurface();
+    method public abstract boolean isReprocessible();
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -13163,6 +13168,8 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createReprocessibleCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -13413,6 +13420,7 @@
     method public int describeContents();
     method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method public java.lang.Object getTag();
+    method public boolean isReprocess();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
@@ -13617,6 +13625,13 @@
     field public static final int SCORE_MIN = 1; // 0x1
   }
 
+  public final class InputConfiguration {
+    ctor public InputConfiguration(int, int, int);
+    method public int getFormat();
+    method public int getHeight();
+    method public int getWidth();
+  }
+
   public final class LensShadingMap {
     method public void copyGainFactors(float[], int);
     method public int getColumnCount();
@@ -28665,7 +28680,7 @@
     field public static final int START_WITH_SCREENSHOT = 2; // 0x2
   }
 
-  public abstract class VoiceInteractionSession implements android.view.KeyEvent.Callback {
+  public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
     ctor public VoiceInteractionSession(android.content.Context);
     ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
     method public void finish();
@@ -28681,6 +28696,7 @@
     method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
     method public void onCompleteVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
+    method public void onConfigurationChanged(android.content.res.Configuration);
     method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onCreate(android.os.Bundle, int);
     method public android.view.View onCreateContentView();
@@ -28693,10 +28709,12 @@
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
     method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public void onLowMemory();
     method public abstract void onPickOption(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.app.VoiceInteractor.PickOptionRequest.Option[], android.os.Bundle);
     method public void onShow(android.os.Bundle, int);
     method public void onTaskFinished(android.content.Intent, int);
     method public void onTaskStarted(android.content.Intent, int);
+    method public void onTrimMemory(int);
     method public void setContentView(android.view.View);
     method public void setKeepAwake(boolean);
     method public void setTheme(int);
@@ -35569,6 +35587,8 @@
     field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
     field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
     field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+    field public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6; // 0x6
+    field public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7; // 0x7
     field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
     field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
     field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
diff --git a/api/removed.txt b/api/removed.txt
index 0c433c3..326b05d 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,3 +1,12 @@
+package android {
+
+  public static final class Manifest.permission {
+    field public static final java.lang.String SUBSCRIBED_FEEDS_READ = "android.permission.SUBSCRIBED_FEEDS_READ";
+    field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE";
+  }
+
+}
+
 package android.content.pm {
 
   public class PackageInfo implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 1a28546..deaf916 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3644,6 +3644,7 @@
     method public int getMemoryClass();
     method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
     method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
+    method public int getPackageImportance(java.lang.String);
     method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
     method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
     method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
@@ -3736,9 +3737,11 @@
     field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
     field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
     field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
+    field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
     field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
     field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
     field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
+    field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
     field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
     field public static final int REASON_PROVIDER_IN_USE = 1; // 0x1
     field public static final int REASON_SERVICE_IN_USE = 2; // 0x2
@@ -13344,6 +13347,8 @@
     method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
     method public abstract android.hardware.camera2.CameraDevice getDevice();
+    method public abstract android.view.Surface getInputSurface();
+    method public abstract boolean isReprocessible();
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -13459,6 +13464,8 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createReprocessibleCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -13709,6 +13716,7 @@
     method public int describeContents();
     method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method public java.lang.Object getTag();
+    method public boolean isReprocess();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
@@ -13913,6 +13921,13 @@
     field public static final int SCORE_MIN = 1; // 0x1
   }
 
+  public final class InputConfiguration {
+    ctor public InputConfiguration(int, int, int);
+    method public int getFormat();
+    method public int getHeight();
+    method public int getWidth();
+  }
+
   public final class LensShadingMap {
     method public void copyGainFactors(float[], int);
     method public int getColumnCount();
@@ -30761,7 +30776,7 @@
     field public static final int START_WITH_SCREENSHOT = 2; // 0x2
   }
 
-  public abstract class VoiceInteractionSession implements android.view.KeyEvent.Callback {
+  public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
     ctor public VoiceInteractionSession(android.content.Context);
     ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
     method public void finish();
@@ -30777,6 +30792,7 @@
     method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
     method public void onCompleteVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
+    method public void onConfigurationChanged(android.content.res.Configuration);
     method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
     method public void onCreate(android.os.Bundle, int);
     method public android.view.View onCreateContentView();
@@ -30789,10 +30805,12 @@
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
     method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public void onLowMemory();
     method public abstract void onPickOption(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.app.VoiceInteractor.PickOptionRequest.Option[], android.os.Bundle);
     method public void onShow(android.os.Bundle, int);
     method public void onTaskFinished(android.content.Intent, int);
     method public void onTaskStarted(android.content.Intent, int);
+    method public void onTrimMemory(int);
     method public void setContentView(android.view.View);
     method public void setKeepAwake(boolean);
     method public void setTheme(int);
@@ -38190,6 +38208,8 @@
     field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
     field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
     field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+    field public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6; // 0x6
+    field public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7; // 0x7
     field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
     field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
     field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 0c433c3..326b05d 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,12 @@
+package android {
+
+  public static final class Manifest.permission {
+    field public static final java.lang.String SUBSCRIBED_FEEDS_READ = "android.permission.SUBSCRIBED_FEEDS_READ";
+    field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE";
+  }
+
+}
+
 package android.content.pm {
 
   public class PackageInfo implements android.os.Parcelable {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 908d46e..fa28143 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -124,6 +124,7 @@
                 "       am restart\n" +
                 "       am idle-maintenance\n" +
                 "       am screen-compat [on|off] <PACKAGE>\n" +
+                "       am package-importance <PACKAGE>\n" +
                 "       am to-uri [INTENT]\n" +
                 "       am to-intent-uri [INTENT]\n" +
                 "       am to-app-uri [INTENT]\n" +
@@ -233,6 +234,8 @@
                 "\n" +
                 "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
                 "\n" +
+                "am package-importance: print current importance of <PACKAGE>.\n" +
+                "\n" +
                 "am to-uri: print the given Intent specification as a URI.\n" +
                 "\n" +
                 "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
@@ -365,6 +368,8 @@
             runIdleMaintenance();
         } else if (op.equals("screen-compat")) {
             runScreenCompat();
+        } else if (op.equals("package-importance")) {
+            runPackageImportance();
         } else if (op.equals("to-uri")) {
             runToUri(0);
         } else if (op.equals("to-intent-uri")) {
@@ -1604,6 +1609,16 @@
         } while (packageName != null);
     }
 
+    private void runPackageImportance() throws Exception {
+        String packageName = nextArgRequired();
+        try {
+            int procState = mAm.getPackageProcessState(packageName);
+            System.out.println(
+                    ActivityManager.RunningAppProcessInfo.procStateToImportance(procState));
+        } catch (RemoteException e) {
+        }
+    }
+
     private void runToUri(int flags) throws Exception {
         Intent intent = makeIntent(UserHandle.USER_CURRENT);
         System.out.println(intent.toUri(flags));
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2b35cd4..51ececc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2010,27 +2010,47 @@
         public int lastTrimLevel;
 
         /**
-         * Constant for {@link #importance}: this process is running the
-         * foreground UI.
+         * Constant for {@link #importance}: This process is running the
+         * foreground UI; that is, it is the thing currently at the top of the screen
+         * that the user is interacting with.
          */
         public static final int IMPORTANCE_FOREGROUND = 100;
         
         /**
-         * Constant for {@link #importance}: this process is running something
+         * Constant for {@link #importance}: This process is running a foreground
+         * service, for example to perform music playback even while the user is
+         * not immediately in the app.  This generally indicates that the process
+         * is doing something the user actively cares about.
+         */
+        public static final int IMPORTANCE_FOREGROUND_SERVICE = 125;
+
+        /**
+         * Constant for {@link #importance}: This process is running the foreground
+         * UI, but the device is asleep so it is not visible to the user.  This means
+         * the user is not really aware of the process, because they can not see or
+         * interact with it, but it is quite important because it what they expect to
+         * return to once unlocking the device.
+         */
+        public static final int IMPORTANCE_TOP_SLEEPING = 150;
+
+        /**
+         * Constant for {@link #importance}: This process is running something
          * that is actively visible to the user, though not in the immediate
-         * foreground.
+         * foreground.  This may be running a window that is behind the current
+         * foreground (so paused and with its state saved, not interacting with
+         * the user, but visible to them to some degree); it may also be running
+         * other services under the system's control that it inconsiders important.
          */
         public static final int IMPORTANCE_VISIBLE = 200;
         
         /**
-         * Constant for {@link #importance}: this process is running something
-         * that is considered to be actively perceptible to the user.  An
-         * example would be an application performing background music playback.
+         * Constant for {@link #importance}: This process is not something the user
+         * is directly aware of, but is otherwise perceptable to them to some degree.
          */
         public static final int IMPORTANCE_PERCEPTIBLE = 130;
         
         /**
-         * Constant for {@link #importance}: this process is running an
+         * Constant for {@link #importance}: This process is running an
          * application that can not save its state, and thus can't be killed
          * while in the background.
          * @hide
@@ -2038,42 +2058,51 @@
         public static final int IMPORTANCE_CANT_SAVE_STATE = 170;
         
         /**
-         * Constant for {@link #importance}: this process is contains services
-         * that should remain running.
+         * Constant for {@link #importance}: This process is contains services
+         * that should remain running.  These are background services apps have
+         * started, not something the user is aware of, so they may be killed by
+         * the system relatively freely (though it is generally desired that they
+         * stay running as long as they want to).
          */
         public static final int IMPORTANCE_SERVICE = 300;
         
         /**
-         * Constant for {@link #importance}: this process process contains
+         * Constant for {@link #importance}: This process process contains
          * background code that is expendable.
          */
         public static final int IMPORTANCE_BACKGROUND = 400;
         
         /**
-         * Constant for {@link #importance}: this process is empty of any
+         * Constant for {@link #importance}: This process is empty of any
          * actively running code.
          */
         public static final int IMPORTANCE_EMPTY = 500;
 
         /**
-         * Constant for {@link #importance}: this process does not exist.
+         * Constant for {@link #importance}: This process does not exist.
          */
         public static final int IMPORTANCE_GONE = 1000;
 
         /** @hide */
         public static int procStateToImportance(int procState) {
-            if (procState >= ActivityManager.PROCESS_STATE_HOME) {
-                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
-            } else if (procState >= ActivityManager.PROCESS_STATE_SERVICE) {
-                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
-            } else if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
-                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
-            } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
-            } else if (procState >= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+            if (procState == PROCESS_STATE_NONEXISTENT) {
+                return IMPORTANCE_GONE;
+            } else if (procState >= PROCESS_STATE_HOME) {
+                return IMPORTANCE_BACKGROUND;
+            } else if (procState >= PROCESS_STATE_SERVICE) {
+                return IMPORTANCE_SERVICE;
+            } else if (procState > PROCESS_STATE_HEAVY_WEIGHT) {
+                return IMPORTANCE_CANT_SAVE_STATE;
+            } else if (procState >= PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                return IMPORTANCE_PERCEPTIBLE;
+            } else if (procState >= PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                return IMPORTANCE_VISIBLE;
+            } else if (procState >= PROCESS_STATE_TOP_SLEEPING) {
+                return IMPORTANCE_TOP_SLEEPING;
+            } else if (procState >= PROCESS_STATE_FOREGROUND_SERVICE) {
+                return IMPORTANCE_FOREGROUND_SERVICE;
             } else {
-                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+                return IMPORTANCE_FOREGROUND;
             }
         }
 
@@ -2253,6 +2282,22 @@
     }
 
     /**
+     * Return the importance of a given package name, based on the processes that are
+     * currently running.  The return value is one of the importance constants defined
+     * in {@link RunningAppProcessInfo}, giving you the highest importance of all the
+     * processes that this package has code running inside of.  If there are no processes
+     * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned.
+     */
+    public int getPackageImportance(String packageName) {
+        try {
+            int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName);
+            return RunningAppProcessInfo.procStateToImportance(procState);
+        } catch (RemoteException e) {
+            return RunningAppProcessInfo.IMPORTANCE_GONE;
+        }
+    }
+
+    /**
      * Return global memory state information for the calling process.  This
      * does not fill in all fields of the {@link RunningAppProcessInfo}.  The
      * only fields that will be filled in are
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f63d13c..256d87d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -106,9 +106,24 @@
         }
     }
 
-    static public void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg) {
+    static public void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg,
+            String tag) {
         try {
-            getDefault().noteWakeupAlarm(ps.getTarget(), sourceUid, sourcePkg);
+            getDefault().noteWakeupAlarm(ps.getTarget(), sourceUid, sourcePkg, tag);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    static public void noteAlarmStart(PendingIntent ps, int sourceUid, String tag) {
+        try {
+            getDefault().noteAlarmStart(ps.getTarget(), sourceUid, tag);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    static public void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) {
+        try {
+            getDefault().noteAlarmFinish(ps.getTarget(), sourceUid, tag);
         } catch (RemoteException ex) {
         }
     }
@@ -1375,7 +1390,30 @@
                     data.readStrongBinder());
             int sourceUid = data.readInt();
             String sourcePkg = data.readString();
-            noteWakeupAlarm(is, sourceUid, sourcePkg);
+            String tag = data.readString();
+            noteWakeupAlarm(is, sourceUid, sourcePkg, tag);
+            reply.writeNoException();
+            return true;
+        }
+
+        case NOTE_ALARM_START_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IIntentSender is = IIntentSender.Stub.asInterface(
+                    data.readStrongBinder());
+            int sourceUid = data.readInt();
+            String tag = data.readString();
+            noteAlarmStart(is, sourceUid, tag);
+            reply.writeNoException();
+            return true;
+        }
+
+        case NOTE_ALARM_FINISH_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IIntentSender is = IIntentSender.Stub.asInterface(
+                    data.readStrongBinder());
+            int sourceUid = data.readInt();
+            String tag = data.readString();
+            noteAlarmFinish(is, sourceUid, tag);
             reply.writeNoException();
             return true;
         }
@@ -2461,6 +2499,15 @@
             reply.writeNoException();
             return true;
         }
+
+        case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String pkg = data.readString();
+            int res = getPackageProcessState(pkg);
+            reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3304,7 +3351,7 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(stackBoxId);
         r.writeToParcel(data, 0);
-        mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -3360,7 +3407,7 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(stackId);
-        mRemote.transact(SET_FOCUSED_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SET_FOCUSED_STACK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -3384,7 +3431,7 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(listener.asBinder());
-        mRemote.transact(REGISTER_TASK_STACK_LISTENER_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(REGISTER_TASK_STACK_LISTENER_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -4214,16 +4261,37 @@
         mRemote.transact(ENTER_SAFE_MODE_TRANSACTION, data, null, 0);
         data.recycle();
     }
-    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg)
+    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(sender.asBinder());
         data.writeInt(sourceUid);
         data.writeString(sourcePkg);
+        data.writeString(tag);
         mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0);
         data.recycle();
     }
+    public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(sender.asBinder());
+        data.writeInt(sourceUid);
+        data.writeString(tag);
+        mRemote.transact(NOTE_ALARM_START_TRANSACTION, data, null, 0);
+        data.recycle();
+    }
+    public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(sender.asBinder());
+        data.writeInt(sourceUid);
+        data.writeString(tag);
+        mRemote.transact(NOTE_ALARM_FINISH_TRANSACTION, data, null, 0);
+        data.recycle();
+    }
     public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -5486,7 +5554,7 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         values.writeToParcel(data, 0);
-        mRemote.transact(SET_TASK_DESCRIPTION_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SET_TASK_DESCRIPTION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5499,7 +5567,7 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(taskId);
         data.writeInt(resizeable ? 1 : 0);
-        mRemote.transact(SET_TASK_RESIZEABLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SET_TASK_RESIZEABLE_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5513,7 +5581,7 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(taskId);
         r.writeToParcel(data, 0);
-        mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5545,7 +5613,7 @@
             data.writeInt(1);
             data.writeBundle(options.toBundle());
         }
-        mRemote.transact(START_IN_PLACE_ANIMATION_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(START_IN_PLACE_ANIMATION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5586,8 +5654,7 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(BACKGROUND_RESOURCES_RELEASED_TRANSACTION, data, reply,
-                IBinder.FLAG_ONEWAY);
+        mRemote.transact(BACKGROUND_RESOURCES_RELEASED_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5599,8 +5666,7 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION, data, reply,
-                IBinder.FLAG_ONEWAY);
+        mRemote.transact(NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5612,8 +5678,7 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(NOTIFY_ENTER_ANIMATION_COMPLETE_TRANSACTION, data, reply,
-                IBinder.FLAG_ONEWAY);
+        mRemote.transact(NOTIFY_ENTER_ANIMATION_COMPLETE_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -5703,11 +5768,25 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(userId);
         data.writeStringArray(packages);
-        mRemote.transact(UPDATE_LOCK_TASK_PACKAGES_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(UPDATE_LOCK_TASK_PACKAGES_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
     }
 
+    @Override
+    public int getPackageProcessState(String packageName) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        mRemote.transact(GET_PACKAGE_PROCESS_STATE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9ddfd88..907ae26 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -17,6 +17,8 @@
 package android.app;
 
 import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.XmlRes;
 import android.content.ComponentName;
@@ -733,13 +735,16 @@
         }
     }
 
-    @Override public Drawable getDrawable(String packageName, @DrawableRes int resid,
-                                          ApplicationInfo appInfo) {
-        ResourceName name = new ResourceName(packageName, resid);
-        Drawable dr = getCachedIcon(name);
-        if (dr != null) {
-            return dr;
+    @Nullable
+    @Override
+    public Drawable getDrawable(String packageName, @DrawableRes int resId,
+            @Nullable ApplicationInfo appInfo) {
+        final ResourceName name = new ResourceName(packageName, resId);
+        final Drawable cachedIcon = getCachedIcon(name);
+        if (cachedIcon != null) {
+            return cachedIcon;
         }
+
         if (appInfo == null) {
             try {
                 appInfo = getApplicationInfo(packageName, sDefaultFlags);
@@ -747,36 +752,44 @@
                 return null;
             }
         }
-        try {
-            Resources r = getResourcesForApplication(appInfo);
-            dr = r.getDrawable(resid);
-            if (false) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resid)
-                      + " from package " + packageName
-                      + ": app scale=" + r.getCompatibilityInfo().applicationScale
-                      + ", caller scale=" + mContext.getResources().getCompatibilityInfo().applicationScale,
-                      e);
+
+        if (resId != 0) {
+            try {
+                final Resources r = getResourcesForApplication(appInfo);
+                final Drawable dr = r.getDrawable(resId, null);
+                if (dr != null) {
+                    putCachedIcon(name, dr);
+                }
+
+                if (false) {
+                    RuntimeException e = new RuntimeException("here");
+                    e.fillInStackTrace();
+                    Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resId)
+                                    + " from package " + packageName
+                                    + ": app scale=" + r.getCompatibilityInfo().applicationScale
+                                    + ", caller scale=" + mContext.getResources()
+                                    .getCompatibilityInfo().applicationScale,
+                            e);
+                }
+                if (DEBUG_ICONS)
+                    Log.v(TAG, "Getting drawable 0x"
+                            + Integer.toHexString(resId) + " from " + r
+                            + ": " + dr);
+            } catch (NameNotFoundException e) {
+                Log.w("PackageManager", "Failure retrieving resources for "
+                        + appInfo.packageName);
+            } catch (Resources.NotFoundException e) {
+                Log.w("PackageManager", "Failure retrieving resources for "
+                        + appInfo.packageName + ": " + e.getMessage());
+            } catch (Exception e) {
+                // If an exception was thrown, fall through to return
+                // default icon.
+                Log.w("PackageManager", "Failure retrieving icon 0x"
+                        + Integer.toHexString(resId) + " in package "
+                        + packageName, e);
             }
-            if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
-                                   + Integer.toHexString(resid) + " from " + r
-                                   + ": " + dr);
-            putCachedIcon(name, dr);
-            return dr;
-        } catch (NameNotFoundException e) {
-            Log.w("PackageManager", "Failure retrieving resources for "
-                  + appInfo.packageName);
-        } catch (Resources.NotFoundException e) {
-            Log.w("PackageManager", "Failure retrieving resources for "
-                  + appInfo.packageName + ": " + e.getMessage());
-        } catch (RuntimeException e) {
-            // If an exception was thrown, fall through to return
-            // default icon.
-            Log.w("PackageManager", "Failure retrieving icon 0x"
-                  + Integer.toHexString(resid) + " in package "
-                  + packageName, e);
         }
+
         return null;
     }
 
@@ -923,19 +936,21 @@
         return label;
     }
 
-    @Override public Resources getResourcesForActivity(
-        ComponentName activityName) throws NameNotFoundException {
+    @Override
+    public Resources getResourcesForActivity(ComponentName activityName)
+            throws NameNotFoundException {
         return getResourcesForApplication(
             getActivityInfo(activityName, sDefaultFlags).applicationInfo);
     }
 
-    @Override public Resources getResourcesForApplication(
-        ApplicationInfo app) throws NameNotFoundException {
+    @Override
+    public Resources getResourcesForApplication(@NonNull ApplicationInfo app)
+            throws NameNotFoundException {
         if (app.packageName.equals("system")) {
             return mContext.mMainThread.getSystemContext().getResources();
         }
         final boolean sameUid = (app.uid == Process.myUid());
-        Resources r = mContext.mMainThread.getTopLevelResources(
+        final Resources r = mContext.mMainThread.getTopLevelResources(
                 sameUid ? app.sourceDir : app.publicSourceDir,
                 sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
                 app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
@@ -946,8 +961,9 @@
         throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
     }
 
-    @Override public Resources getResourcesForApplication(
-        String appPackageName) throws NameNotFoundException {
+    @Override
+    public Resources getResourcesForApplication(String appPackageName)
+            throws NameNotFoundException {
         return getResourcesForApplication(
             getApplicationInfo(appPackageName, sDefaultFlags));
     }
@@ -999,13 +1015,14 @@
         mPM = pm;
     }
 
-    private Drawable getCachedIcon(ResourceName name) {
+    @Nullable
+    private Drawable getCachedIcon(@NonNull ResourceName name) {
         synchronized (sSync) {
-            WeakReference<Drawable.ConstantState> wr = sIconCache.get(name);
+            final WeakReference<Drawable.ConstantState> wr = sIconCache.get(name);
             if (DEBUG_ICONS) Log.v(TAG, "Get cached weak drawable ref for "
                                    + name + ": " + wr);
             if (wr != null) {   // we have the activity
-                Drawable.ConstantState state = wr.get();
+                final Drawable.ConstantState state = wr.get();
                 if (state != null) {
                     if (DEBUG_ICONS) {
                         Log.v(TAG, "Get cached drawable state for " + name + ": " + state);
@@ -1025,9 +1042,9 @@
         return null;
     }
 
-    private void putCachedIcon(ResourceName name, Drawable dr) {
+    private void putCachedIcon(@NonNull ResourceName name, @NonNull Drawable dr) {
         synchronized (sSync) {
-            sIconCache.put(name, new WeakReference<Drawable.ConstantState>(dr.getConstantState()));
+            sIconCache.put(name, new WeakReference<>(dr.getConstantState()));
             if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable state for " + name + ": " + dr);
         }
     }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 4a1d6ff..59de281 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -262,7 +262,11 @@
 
     public void enterSafeMode() throws RemoteException;
     
-    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg)
+    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag)
+            throws RemoteException;
+    public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag)
+            throws RemoteException;
+    public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag)
             throws RemoteException;
 
     public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException;
@@ -490,6 +494,8 @@
             throws RemoteException;
     public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
 
+    public int getPackageProcessState(String packageName) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -825,4 +831,7 @@
     int DUMP_HEAP_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+288;
     int SET_VOICE_KEEP_AWAKE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+289;
     int UPDATE_LOCK_TASK_PACKAGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+290;
+    int NOTE_ALARM_START_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+291;
+    int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
+    int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
 }
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ce83028..6b6f026 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -17,21 +17,31 @@
 package android.hardware.camera2;
 
 import android.os.Handler;
+import android.view.Surface;
 import java.util.List;
 
+
 /**
- * A configured capture session for a {@link CameraDevice}, used for capturing
- * images from the camera.
+ * A configured capture session for a {@link CameraDevice}, used for capturing images from the
+ * camera or reprocessing images captured from the camera in the same session previously.
  *
  * <p>A CameraCaptureSession is created by providing a set of target output surfaces to
- * {@link CameraDevice#createCaptureSession createCaptureSession}. Once created, the session is
- * active until a new session is created by the camera device, or the camera device is closed.</p>
+ * {@link CameraDevice#createCaptureSession createCaptureSession}, or by providing an
+ * {@link android.hardware.camera2.params.InputConfiguration} and a set of target output surfaces to
+ * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} for a
+ * reprocessible capture session. Once created, the session is active until a new session is
+ * created by the camera device, or the camera device is closed.</p>
+ *
+ * <p>All capture sessions can be used for capturing images from the camera but only reprocessible
+ * capture sessions can reprocess images captured from the camera in the same session previously.
+ * </p>
  *
  * <p>Creating a session is an expensive operation and can take several hundred milliseconds, since
  * it requires configuring the camera device's internal pipelines and allocating memory buffers for
  * sending images to the desired targets. Therefore the setup is done asynchronously, and
- * {@link CameraDevice#createCaptureSession createCaptureSession} will send the ready-to-use
- * CameraCaptureSession to the provided listener's
+ * {@link CameraDevice#createCaptureSession createCaptureSession} and
+ * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} will
+ * send the ready-to-use CameraCaptureSession to the provided listener's
  * {@link CameraCaptureSession.StateCallback#onConfigured onConfigured} callback. If configuration
  * cannot be completed, then the
  * {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is called, and the
@@ -77,6 +87,12 @@
      * {@link #setRepeatingBurst}, and will be processed as soon as the current
      * repeat/repeatBurst processing completes.</p>
      *
+     * <p>All capture sessions can be used for capturing images from the camera but only capture
+     * sessions created by
+     * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession}
+     * can submit reprocess capture requests. Submitting a reprocess request to a regular capture
+     * session will result in an {@link IllegalArgumentException}.</p>
+     *
      * @param request the settings for this capture
      * @param listener The callback object to notify once this request has been
      * processed. If null, no metadata will be produced for this capture,
@@ -94,7 +110,9 @@
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
      * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
-     *                                  configured as outputs for this session. Or if the handler is
+     *                                  configured as outputs for this session. Or if a reprocess
+     *                                  capture request is submitted in a non-reprocessible capture
+     *                                  session. Or if the handler is
      *                                  null, the listener is not null, and the calling thread has
      *                                  no looper.
      *
@@ -102,6 +120,7 @@
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #abortCaptures
+     * @see CameraDevice#createReprocessibleCaptureSession
      */
     public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
             throws CameraAccessException;
@@ -121,6 +140,13 @@
      * {@link #capture} repeatedly is that this method guarantees that no
      * other requests will be interspersed with the burst.</p>
      *
+     * <p>All capture sessions can be used for capturing images from the camera but only capture
+     * sessions created by
+     * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession}
+     * can submit reprocess capture requests. The list of requests must all be capturing images from
+     * the camera or all be reprocess capture requests. Submitting a reprocess request to a regular
+     * capture session will result in an {@link IllegalArgumentException}.</p>
+     *
      * @param requests the list of settings for this burst capture
      * @param listener The callback object to notify each time one of the
      * requests in the burst has been processed. If null, no metadata will be
@@ -138,9 +164,13 @@
      * @throws IllegalStateException if this session is no longer active, either because the session
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
-     * @throws IllegalArgumentException If the requests target no Surfaces or Surfaces not currently
-     *                                  configured as outputs. Or if the handler is null, the
-     *                                  listener is not null, and the calling thread has no looper.
+     * @throws IllegalArgumentException If the requests target no Surfaces, or target Surfaces not
+     *                                  currently configured as outputs. Or if a reprocess
+     *                                  capture request is submitted in a non-reprocessible capture
+     *                                  session. Or if the list of requests contains both requests
+     *                                  to capture images from the camera and reprocess capture
+     *                                  requests. Or if the handler is null, the listener is not
+     *                                  null, and the calling thread has no looper.
      *
      * @see #capture
      * @see #setRepeatingRequest
@@ -175,6 +205,14 @@
      * in-progress burst will be completed before the new repeat request will be
      * used.</p>
      *
+     * <p>This method does not support reprocess capture requests because each reprocess
+     * {@link CaptureRequest} must be created from the {@link TotalCaptureResult} that matches
+     * the input image to be reprocessed. This is either the {@link TotalCaptureResult} of capture
+     * that is sent for reprocessing, or one of the {@link TotalCaptureResult TotalCaptureResults}
+     * of a set of captures, when data from the whole set is combined by the application into a
+     * single reprocess input image. The request must be capturing images from the camera. If a
+     * reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
+     *
      * @param request the request to repeat indefinitely
      * @param listener The callback object to notify every time the
      * request finishes processing. If null, no metadata will be
@@ -193,9 +231,10 @@
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
      * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces that are
-     *                                  not currently configured as outputs. Or if the handler is
-     *                                  null, the listener is not null, and the calling thread has
-     *                                  no looper. Or if no requests were passed in.
+     *                                  not currently configured as outputs. Or if the request is
+     *                                  a reprocess capture request. Or if the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper.
+     *                                  Or if no requests were passed in.
      *
      * @see #capture
      * @see #captureBurst
@@ -235,6 +274,14 @@
      * in-progress burst will be completed before the new repeat burst will be
      * used.</p>
      *
+     * <p>This method does not support reprocess capture requests because each reprocess
+     * {@link CaptureRequest} must be created from the {@link TotalCaptureResult} that matches
+     * the input image to be reprocessed. This is either the {@link TotalCaptureResult} of capture
+     * that is sent for reprocessing, or one of the {@link TotalCaptureResult TotalCaptureResults}
+     * of a set of captures, when data from the whole set is combined by the application into a
+     * single reprocess input image. The request must be capturing images from the camera. If a
+     * reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
+     *
      * @param requests the list of requests to cycle through indefinitely
      * @param listener The callback object to notify each time one of the
      * requests in the repeating bursts has finished processing. If null, no
@@ -253,7 +300,8 @@
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
      * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces not
-     *                                  currently configured as outputs. Or if the handler is null,
+     *                                  currently configured as outputs. Or if one of the requests
+     *                                  is a reprocess capture request. Or if the handler is null,
      *                                  the listener is not null, and the calling thread has no
      *                                  looper. Or if no requests were passed in.
      *
@@ -298,9 +346,10 @@
      * request or a repeating burst is set, it will be cleared.</p>
      *
      * <p>This method is the fastest way to switch the camera device to a new session with
-     * {@link CameraDevice#createCaptureSession}, at the cost of discarding in-progress work. It
-     * must be called before the new session is created. Once all pending requests are either
-     * completed or thrown away, the {@link StateCallback#onReady} callback will be called,
+     * {@link CameraDevice#createCaptureSession} or
+     * {@link CameraDevice#createReprocessibleCaptureSession}, at the cost of discarding in-progress
+     * work. It must be called before the new session is created. Once all pending requests are
+     * either completed or thrown away, the {@link StateCallback#onReady} callback will be called,
      * if the session has not been closed. Otherwise, the {@link StateCallback#onClosed}
      * callback will be fired when a new session is created by the camera device.</p>
      *
@@ -321,10 +370,39 @@
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see CameraDevice#createCaptureSession
+     * @see CameraDevice#createReprocessibleCaptureSession
      */
     public abstract void abortCaptures() throws CameraAccessException;
 
     /**
+     * Return if the application can submit reprocess capture requests with this camera capture
+     * session.
+     *
+     * @return {@code true} if the application can submit reprocess capture requests with this
+     *         camera capture session. {@code false} otherwise.
+     *
+     * @see CameraDevice#createReprocessibleCaptureSession
+     */
+    public abstract boolean isReprocessible();
+
+    /**
+     * Get the input Surface associated with a reprocessible capture session.
+     *
+     * <p>Each reprocessible capture session has an input {@link Surface} where the reprocess
+     * capture requests get the input images from, rather than the camera device. The application
+     * can create a {@link android.media.ImageWriter} with this input {@link Surface} and use it to
+     * provide input images for reprocess capture requests.</p>
+     *
+     * @return The {@link Surface} where reprocessing capture requests get the input images from. If
+     *         this is not a reprocess capture session, {@code null} will be returned.
+     *
+     * @see CameraDevice#createReprocessibleCaptureSession
+     * @see android.media.ImageWriter
+     * @see android.media.ImageReader
+     */
+    public abstract Surface getInputSurface();
+
+    /**
      * Close this capture session asynchronously.
      *
      * <p>Closing a session frees up the target output Surfaces of the session for reuse with either
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index fd4cf3c..51b326b 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.os.Handler;
@@ -135,7 +136,7 @@
      *
      * <p>The active capture session determines the set of potential output Surfaces for
      * the camera device for each capture request. A given request may use all
-     * or a only some of the outputs. Once the CameraCaptureSession is created, requests can be
+     * or only some of the outputs. Once the CameraCaptureSession is created, requests can be
      * can be submitted with {@link CameraCaptureSession#capture capture},
      * {@link CameraCaptureSession#captureBurst captureBurst},
      * {@link CameraCaptureSession#setRepeatingRequest setRepeatingRequest}, or
@@ -393,6 +394,75 @@
             List<OutputConfiguration> outputConfigurations,
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException;
+    /**
+     * Create a new reprocessible camera capture session by providing the desired reprocessing
+     * input Surface configuration and the target output set of Surfaces to the camera device.
+     *
+     * <p>If a camera device supports YUV reprocessing
+     * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or OPAQUE
+     * reprocessing
+     * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING}), besides
+     * the capture session created via {@link #createCaptureSession}, the application can also
+     * create a reprocessible capture session to submit reprocess capture requests in addition to
+     * regular capture requests. A reprocess capture request takes the next available buffer from
+     * the session's input Surface, and sends it through the camera device's processing pipeline
+     * again, to produce buffers for the request's target output Surfaces. No new image data is
+     * captured for a reprocess request. However the input buffer provided by
+     * the application must be captured previously by the same camera device in the same session
+     * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output
+     * images).</p>
+     *
+     * <p>The active reprocessible capture session determines an input {@link Surface} and the set
+     * of potential output Surfaces for the camera devices for each capture request. The application
+     * can use {@link #createCaptureRequest} to create regular capture requests to capture new
+     * images from the camera device, and use {@link #createReprocessCaptureRequest} to create
+     * reprocess capture requests to process buffers from the input {@link Surface}. A request may
+     * use all or only some of the outputs. All the output Surfaces in one capture request will come
+     * from the same source, either from a new capture by the camera device, or from the input
+     * Surface depending on if the request is a reprocess capture request.</p>
+     *
+     * <p>Input formats and sizes supported by the camera device can be queried via
+     * {@link StreamConfigurationMap#getInputFormats} and
+     * {@link StreamConfigurationMap#getInputSizes}. For each supported input format, the camera
+     * device supports a set of output formats and sizes for reprocessing that can be queried via
+     * {@link StreamConfigurationMap#getValidOutputFormatsForInput} and
+     * {@link StreamConfigurationMap#getOutputSizes}. While output Surfaces with formats that
+     * aren't valid reprocess output targets for the input configuration can be part of a session,
+     * they cannot be used as targets for a reprocessing request.</p>
+     *
+     * <p>Since the application cannot access {@link android.graphics.ImageFormat#PRIVATE} images
+     * directly, an output Surface created by {@link android.media.ImageReader#newOpaqueInstance}
+     * will be considered as intended to be used for reprocessing input and thus the
+     * {@link android.media.ImageReader} size must match one of the supported input sizes for
+     * {@link android.graphics.ImageFormat#PRIVATE} format. Otherwise, creating a reprocessible
+     * capture session will fail.</p>
+     *
+     * @param inputConfig The configuration for the input {@link Surface}
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured image data.
+     * @param callback The callback to notify about the status of the new capture session.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+     *                                  of output Surfaces do not meet the requirements, the
+     *                                  callback is null, or the handler is null but the current
+     *                                  thread has no looper.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see CameraCaptureSession
+     * @see StreamConfigurationMap#getInputFormats
+     * @see StreamConfigurationMap#getInputSizes
+     * @see StreamConfigurationMap#getValidOutputFormatsForInput
+     * @see StreamConfigurationMap#getOutputSizes
+     * @see android.media.ImageWriter
+     * @see android.media.ImageReader
+     */
+    public abstract void createReprocessibleCaptureSession(InputConfiguration inputConfig,
+            List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException;
 
     /**
      * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
@@ -423,6 +493,36 @@
             throws CameraAccessException;
 
     /**
+     * <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
+     * {@link TotalCaptureResult}.
+     *
+     * <p>Each reprocess {@link CaptureRequest} processes one buffer from
+     * {@link CameraCaptureSession}'s input {@link Surface} to all output {@link Surface Surfaces}
+     * included in the reprocess capture request. The reprocess input images must be generated from
+     * one or multiple output images captured from the same camera device. The application can
+     * provide input images to camera device via
+     * {{@link android.media.ImageWriter#queueInputImage ImageWriter#queueInputImage}}.
+     * The application must use the capture result of one of those output images to create a
+     * reprocess capture request so that the camera device can use the information to achieve
+     * optimal reprocess image quality.
+     *
+     * @param inputResult The capture result of the output image or one of the output images used
+     *                       to generate the reprocess input image for this capture request.
+     *
+     * @throws IllegalArgumentException if inputResult is null.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see CaptureRequest.Builder
+     * @see TotalCaptureResult
+     * @see CameraDevice#createReprocessibleCaptureSession
+     * @see android.media.ImageWriter
+     */
+    public abstract CaptureRequest.Builder createReprocessCaptureRequest(
+            TotalCaptureResult inputResult) throws CameraAccessException;
+
+    /**
      * Close the connection to this camera device as quickly as possible.
      *
      * <p>Immediately after this call, all calls to the camera device or active session interface
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b513379..1a00a05 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -148,7 +148,7 @@
      * new one provided.</p>
      *
      * <p>The first time a callback is registered, it is immediately called
-     * with the torch mode status of all currently known camera devices.</p>
+     * with the torch mode status of all currently known camera devices with a flash unit.</p>
      *
      * <p>Since this callback will be registered with the camera service, remember to unregister it
      * once it is no longer needed; otherwise the callback will continue to receive events
@@ -524,7 +524,7 @@
      * A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
      *
      * <p>The torch mode becomes unavailable when the camera device it belongs to becomes
-     * unavailable or other camera resouces it needs become busy due to other higher priority
+     * unavailable or other camera resources it needs become busy due to other higher priority
      * camera activities. The torch mode becomes disabled when it was turned off or when the camera
      * device it belongs to is no longer in use and other camera resources it needs are no longer
      * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b8fb8e7..35727e8 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -157,6 +157,7 @@
 
     private final HashSet<Surface> mSurfaceSet;
     private final CameraMetadataNative mSettings;
+    private boolean mIsReprocess;
 
     private Object mUserTag;
 
@@ -168,6 +169,7 @@
     private CaptureRequest() {
         mSettings = new CameraMetadataNative();
         mSurfaceSet = new HashSet<Surface>();
+        mIsReprocess = false;
     }
 
     /**
@@ -179,6 +181,7 @@
     private CaptureRequest(CaptureRequest source) {
         mSettings = new CameraMetadataNative(source.mSettings);
         mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
+        mIsReprocess = source.mIsReprocess;
         mUserTag = source.mUserTag;
     }
 
@@ -187,9 +190,10 @@
      *
      * Used by the Builder to create a mutable CaptureRequest.
      */
-    private CaptureRequest(CameraMetadataNative settings) {
+    private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
         mSettings = CameraMetadataNative.move(settings);
         mSurfaceSet = new HashSet<Surface>();
+        mIsReprocess = isReprocess;
     }
 
     /**
@@ -257,10 +261,27 @@
     }
 
     /**
+     * Determine if this is a reprocess capture request.
+     *
+     * <p>A reprocess capture request produces output images from an input buffer from the
+     * {@link CameraCaptureSession}'s input {@link Surface}. A reprocess capture request can be
+     * created by {@link CameraDevice#createReprocessCaptureRequest}.</p>
+     *
+     * @return {@code true} if this is a reprocess capture request. {@code false} if this is not a
+     * reprocess capture request.
+     *
+     * @see CameraDevice#createReprocessCaptureRequest
+     */
+    public boolean isReprocess() {
+        return mIsReprocess;
+    }
+
+    /**
      * Determine whether this CaptureRequest is equal to another CaptureRequest.
      *
      * <p>A request is considered equal to another is if it's set of key/values is equal, it's
-     * list of output surfaces is equal, and the user tag is equal.</p>
+     * list of output surfaces is equal, the user tag is equal, and the return values of
+     * isReprocess() are equal.</p>
      *
      * @param other Another instance of CaptureRequest.
      *
@@ -276,7 +297,8 @@
         return other != null
                 && Objects.equals(mUserTag, other.mUserTag)
                 && mSurfaceSet.equals(other.mSurfaceSet)
-                && mSettings.equals(other.mSettings);
+                && mSettings.equals(other.mSettings)
+                && mIsReprocess == other.mIsReprocess;
     }
 
     @Override
@@ -323,6 +345,8 @@
             Surface s = (Surface) p;
             mSurfaceSet.add(s);
         }
+
+        mIsReprocess = (in.readInt() == 0) ? false : true;
     }
 
     @Override
@@ -334,6 +358,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         mSettings.writeToParcel(dest, flags);
         dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
+        dest.writeInt(mIsReprocess ? 1 : 0);
     }
 
     /**
@@ -374,8 +399,8 @@
          *
          * @hide
          */
-        public Builder(CameraMetadataNative template) {
-            mRequest = new CaptureRequest(template);
+        public Builder(CameraMetadataNative template, boolean reprocess) {
+            mRequest = new CaptureRequest(template, reprocess);
         }
 
         /**
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d8f92e5..4134d28 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -295,11 +295,18 @@
      * <p>Whenever a request has been processed, regardless of failure or success,
      * it gets a unique frame number assigned to its future result/failure.</p>
      *
-     * <p>This value monotonically increments, starting with 0,
-     * for every new result or failure; and the scope is the lifetime of the
-     * {@link CameraDevice}.</p>
+     * <p>For the same type of request (capturing from the camera device or reprocessing), this
+     * value monotonically increments, starting with 0, for every new result or failure and the
+     * scope is the lifetime of the {@link CameraDevice}. Between different types of requests,
+     * the frame number may not monotonically increment. For example, the frame number of a newer
+     * reprocess result may be smaller than the frame number of an older result of capturing new
+     * images from the camera device, but the frame number of a newer reprocess result will never be
+     * smaller than the frame number of an older reprocess result.</p>
      *
      * @return The frame number
+     *
+     * @see CameraDevice#createCaptureRequest
+     * @see CameraDevice#createReprocessCaptureRequest
      */
     public long getFrameNumber() {
         return mFrameNumber;
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 01f2396..23bfa66 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -16,11 +16,11 @@
 
 package android.hardware.camera2;
 
-import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.CaptureRequest;
-
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.LongParcelable;
+import android.view.Surface;
 
 /** @hide */
 interface ICameraDeviceUser
@@ -68,6 +68,29 @@
     // non-negative value is the stream ID. negative value is status_t
     int createStream(in OutputConfiguration outputConfiguration);
 
+    /**
+     * Create an input stream
+     *
+     * <p>Create an input stream of width, height, and format</p>
+     *
+     * @param width Width of the input buffers
+     * @param height Height of the input buffers
+     * @param format Format of the input buffers. One of HAL_PIXEL_FORMAT_*.
+     *
+     * @return stream ID if it's a non-negative value. status_t if it's a negative value.
+     */
+    int createInputStream(int width, int height, int format);
+
+    /**
+     * Get the surface of the input stream.
+     *
+     * <p>It's valid to call this method only after a stream configuration is completed
+     * successfully and the stream configuration includes a input stream.</p>
+     *
+     * @param surface An output argument for the surface of the input stream buffer queue.
+     */
+    int getInputSurface(out Surface surface);
+
     int createDefaultRequest(int templateId, out CameraMetadataNative request);
 
     int getCameraInfo(out CameraMetadataNative info);
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index e87a2f8..fb5b13c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -44,6 +44,8 @@
     private final int mId;
     private final String mIdString;
 
+    /** Input surface configured by native camera framework based on user-specified configuration */
+    private final Surface mInput;
     /** User-specified set of surfaces used as the configuration outputs */
     private final List<Surface> mOutputs;
     /**
@@ -85,7 +87,7 @@
      * There must be no pending actions
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
-    CameraCaptureSessionImpl(int id, List<Surface> outputs,
+    CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
             CameraCaptureSession.StateCallback callback, Handler stateHandler,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
             Handler deviceStateHandler, boolean configureSuccess) {
@@ -100,6 +102,7 @@
 
         // TODO: extra verification of outputs
         mOutputs = outputs;
+        mInput = input;
         mStateHandler = checkHandler(stateHandler);
         mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
 
@@ -145,8 +148,12 @@
             Handler handler) throws CameraAccessException {
         if (request == null) {
             throw new IllegalArgumentException("request must not be null");
+        } else if (request.isReprocess() && !isReprocessible()) {
+            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
+                    "requests");
         }
 
+
         checkNotClosed();
 
         handler = checkHandler(handler, callback);
@@ -169,6 +176,19 @@
             throw new IllegalArgumentException("requests must have at least one element");
         }
 
+        boolean reprocess = requests.get(0).isReprocess();
+        if (reprocess && !isReprocessible()) {
+            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
+                    "requests");
+        }
+
+        for (int i = 1; i < requests.size(); i++) {
+            if (requests.get(i).isReprocess() != reprocess) {
+                throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
+                        " requests");
+            }
+        }
+
         checkNotClosed();
 
         handler = checkHandler(handler, callback);
@@ -188,8 +208,11 @@
             Handler handler) throws CameraAccessException {
         if (request == null) {
             throw new IllegalArgumentException("request must not be null");
+        } else if (request.isReprocess()) {
+            throw new IllegalArgumentException("repeating reprocess requests are not supported");
         }
 
+
         checkNotClosed();
 
         handler = checkHandler(handler, callback);
@@ -212,6 +235,13 @@
             throw new IllegalArgumentException("requests must have at least one element");
         }
 
+        for (CaptureRequest r : requests) {
+            if (r.isReprocess()) {
+                throw new IllegalArgumentException("repeating reprocess burst requests are not " +
+                        "supported");
+            }
+        }
+
         checkNotClosed();
 
         handler = checkHandler(handler, callback);
@@ -257,6 +287,16 @@
         // The next BUSY -> IDLE set of transitions will mark the end of the abort.
     }
 
+    @Override
+    public boolean isReprocessible() {
+        return mInput != null;
+    }
+
+    @Override
+    public Surface getInputSurface() {
+        return mInput;
+    }
+
     /**
      * Replace this session with another session.
      *
@@ -658,8 +698,8 @@
                     mUnconfigureDrainer.taskStarted();
 
                     try {
-                        mDeviceImpl
-                                .configureOutputsChecked(null); // begin transition to unconfigured
+                        // begin transition to unconfigured
+                        mDeviceImpl.configureStreamsChecked(null, null);
                     } catch (CameraAccessException e) {
                         // OK: do not throw checked exceptions.
                         Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 38f8e39..91388c3 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,7 +28,10 @@
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.ReprocessFormatsMap;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.LongParcelable;
@@ -37,6 +40,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Size;
 import android.util.SparseArray;
 import android.view.Surface;
 
@@ -46,7 +50,8 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.TreeSet;
+import java.util.LinkedList;
+import java.util.TreeMap;
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,9 +83,11 @@
 
     private int mRepeatingRequestId = REQUEST_ID_NONE;
     private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
-    // Map stream IDs to Surfaces
+    // Map stream IDs to input/output configurations
+    private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
+            new SimpleEntry<>(REQUEST_ID_NONE, null);
     private final SparseArray<OutputConfiguration> mConfiguredOutputs =
-            new SparseArray<OutputConfiguration>();
+            new SparseArray<>();
 
     private final String mCameraId;
     private final CameraCharacteristics mCharacteristics;
@@ -320,38 +327,48 @@
         for (Surface s : outputs) {
             outputConfigs.add(new OutputConfiguration(s));
         }
-        configureOutputsChecked(outputConfigs);
+        configureStreamsChecked(/*inputConfig*/null, outputConfigs);
+
     }
 
     /**
-     * Attempt to configure the outputs; the device goes to idle and then configures the
-     * new outputs if possible.
+     * Attempt to configure the input and outputs; the device goes to idle and then configures the
+     * new input and outputs if possible.
      *
-     * <p>The configuration may gracefully fail, if there are too many outputs, if the formats
-     * are not supported, or if the sizes for that format is not supported. In this case this
-     * function will return {@code false} and the unconfigured callback will be fired.</p>
+     * <p>The configuration may gracefully fail, if input configuration is not supported,
+     * if there are too many outputs, if the formats are not supported, or if the sizes for that
+     * format is not supported. In this case this function will return {@code false} and the
+     * unconfigured callback will be fired.</p>
      *
-     * <p>If the configuration succeeds (with 1 or more outputs), then the idle callback is fired.
-     * Unconfiguring the device always fires the idle callback.</p>
+     * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
+     * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
      *
+     * @param inputConfig input configuration or {@code null} for no input
      * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
      * @return whether or not the configuration was successful
      *
      * @throws CameraAccessException if there were any unexpected problems during configuration
      */
-    public boolean configureOutputsChecked(List<OutputConfiguration> outputs)
-            throws CameraAccessException {
+    public boolean configureStreamsChecked(InputConfiguration inputConfig,
+            List<OutputConfiguration> outputs) throws CameraAccessException {
         // Treat a null input the same an empty list
         if (outputs == null) {
             outputs = new ArrayList<OutputConfiguration>();
         }
+        if (outputs.size() == 0 && inputConfig != null) {
+            throw new IllegalArgumentException("cannot configure an input stream without " +
+                    "any output streams");
+        }
+
+        checkInputConfiguration(inputConfig);
+
         boolean success = false;
 
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
             // Streams to create
             HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
-         // Streams to delete
+            // Streams to delete
             List<Integer> deleteList = new ArrayList<Integer>();
 
             // Determine which streams need to be created, which to be deleted
@@ -373,6 +390,24 @@
                 waitUntilIdle();
 
                 mRemoteDevice.beginConfigure();
+
+                // reconfigure the input stream if the input configuration is different.
+                InputConfiguration currentInputConfig = mConfiguredInput.getValue();
+                if (inputConfig != currentInputConfig &&
+                        (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
+                    if (currentInputConfig != null) {
+                        mRemoteDevice.deleteStream(mConfiguredInput.getKey());
+                        mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
+                                REQUEST_ID_NONE, null);
+                    }
+                    if (inputConfig != null) {
+                        int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
+                                inputConfig.getHeight(), inputConfig.getFormat());
+                        mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
+                                streamId, inputConfig);
+                    }
+                }
+
                 // Delete all streams first (to free up HW resources)
                 for (Integer streamId : deleteList) {
                     mRemoteDevice.deleteStream(streamId);
@@ -429,7 +464,7 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionByOutputConfiguration(outConfigurations, callback, handler);
+        createCaptureSessionInternal(null, outConfigurations, callback, handler);
     }
 
     @Override
@@ -437,9 +472,39 @@
             List<OutputConfiguration> outputConfigurations,
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException {
+        if (DEBUG) {
+            Log.d(TAG, "createCaptureSessionByOutputConfiguration");
+        }
+
+        createCaptureSessionInternal(null, outputConfigurations, callback, handler);
+    }
+
+    @Override
+    public void createReprocessibleCaptureSession(InputConfiguration inputConfig,
+            List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException {
+        if (DEBUG) {
+            Log.d(TAG, "createReprocessibleCaptureSession");
+        }
+
+        if (inputConfig == null) {
+            throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
+                    "reprocessible capture session");
+        }
+        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
+        for (Surface surface : outputs) {
+            outConfigurations.add(new OutputConfiguration(surface));
+        }
+        createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler);
+    }
+
+    private void createCaptureSessionInternal(InputConfiguration inputConfig,
+            List<OutputConfiguration> outputConfigurations,
+            CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException {
         synchronized(mInterfaceLock) {
             if (DEBUG) {
-                Log.d(TAG, "createCaptureSession");
+                Log.d(TAG, "createCaptureSessionInternal");
             }
 
             checkIfCameraClosedOrInError();
@@ -453,15 +518,24 @@
             // TODO: dont block for this
             boolean configureSuccess = true;
             CameraAccessException pendingException = null;
+            Surface input = null;
             try {
-                // configure outputs and then block until IDLE
-                configureSuccess = configureOutputsChecked(outputConfigurations);
+                 // configure streams and then block until IDLE
+                configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations);
+                if (inputConfig != null) {
+                    input = new Surface();
+                    mRemoteDevice.getInputSurface(/*out*/input);
+                }
             } catch (CameraAccessException e) {
                 configureSuccess = false;
                 pendingException = e;
+                input = null;
                 if (DEBUG) {
                     Log.v(TAG, "createCaptureSession - failed with exception ", e);
                 }
+            } catch (RemoteException e) {
+                // impossible
+                return;
             }
 
             List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
@@ -470,7 +544,7 @@
             }
             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
             CameraCaptureSessionImpl newSession =
-                    new CameraCaptureSessionImpl(mNextSessionId++,
+                    new CameraCaptureSessionImpl(mNextSessionId++, input,
                             outSurfaces, callback, handler, this, mDeviceHandler,
                             configureSuccess);
 
@@ -512,12 +586,25 @@
             }
 
             CaptureRequest.Builder builder =
-                    new CaptureRequest.Builder(templatedRequest);
+                    new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
 
             return builder;
         }
     }
 
+    @Override
+    public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult)
+            throws CameraAccessException {
+        synchronized(mInterfaceLock) {
+            checkIfCameraClosedOrInError();
+
+            CameraMetadataNative resultMetadata = new
+                    CameraMetadataNative(inputResult.getNativeCopy());
+
+            return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+        }
+    }
+
     public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
             throws CameraAccessException {
         if (DEBUG) {
@@ -810,6 +897,40 @@
         }
     }
 
+    private void checkInputConfiguration(InputConfiguration inputConfig) {
+        if (inputConfig != null) {
+            StreamConfigurationMap configMap = mCharacteristics.get(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+            int[] inputFormats = configMap.getInputFormats();
+            boolean validFormat = false;
+            for (int format : inputFormats) {
+                if (format == inputConfig.getFormat()) {
+                    validFormat = true;
+                }
+            }
+
+            if (validFormat == false) {
+                throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
+                        " is not valid");
+            }
+
+            boolean validSize = false;
+            Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
+            for (Size s : inputSizes) {
+                if (inputConfig.getWidth() == s.getWidth() &&
+                        inputConfig.getHeight() == s.getHeight()) {
+                    validSize = true;
+                }
+            }
+
+            if (validSize == false) {
+                throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
+                        inputConfig.getHeight() + " is not valid");
+            }
+        }
+    }
+
     /**
      * <p>A callback for tracking the progress of a {@link CaptureRequest}
      * submitted to the camera device.</p>
@@ -996,19 +1117,46 @@
     public class FrameNumberTracker {
 
         private long mCompletedFrameNumber = -1;
-        private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
+        private long mCompletedReprocessFrameNumber = -1;
+        /** the skipped frame numbers that belong to regular results */
+        private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
+        /** the skipped frame numbers that belong to reprocess results */
+        private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>();
+        /** frame number -> is reprocess */
+        private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>();
         /** Map frame numbers to list of partial results */
         private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
 
         private void update() {
-            Iterator<Long> iter = mFutureErrorSet.iterator();
+            Iterator iter = mFutureErrorMap.entrySet().iterator();
             while (iter.hasNext()) {
-                long errorFrameNumber = iter.next();
-                if (errorFrameNumber == mCompletedFrameNumber + 1) {
-                    mCompletedFrameNumber++;
-                    iter.remove();
+                TreeMap.Entry pair = (TreeMap.Entry)iter.next();
+                Long errorFrameNumber = (Long)pair.getKey();
+                Boolean reprocess = (Boolean)pair.getValue();
+                Boolean removeError = true;
+                if (reprocess) {
+                    if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) {
+                        mCompletedReprocessFrameNumber = errorFrameNumber;
+                    } else if (mSkippedReprocessFrameNumbers.isEmpty() != true &&
+                            errorFrameNumber == mSkippedReprocessFrameNumbers.element()) {
+                        mCompletedReprocessFrameNumber = errorFrameNumber;
+                        mSkippedReprocessFrameNumbers.remove();
+                    } else {
+                        removeError = false;
+                    }
                 } else {
-                    break;
+                    if (errorFrameNumber == mCompletedFrameNumber + 1) {
+                        mCompletedFrameNumber = errorFrameNumber;
+                    } else if (mSkippedRegularFrameNumbers.isEmpty() != true &&
+                            errorFrameNumber == mSkippedRegularFrameNumbers.element()) {
+                        mCompletedFrameNumber = errorFrameNumber;
+                        mSkippedRegularFrameNumbers.remove();
+                    } else {
+                        removeError = false;
+                    }
+                }
+                if (removeError) {
+                    iter.remove();
                 }
             }
         }
@@ -1017,25 +1165,21 @@
          * This function is called every time when a result or an error is received.
          * @param frameNumber the frame number corresponding to the result or error
          * @param isError true if it is an error, false if it is not an error
+         * @param isReprocess true if it is a reprocess result, false if it is a regular result.
          */
-        public void updateTracker(long frameNumber, boolean isError) {
+        public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) {
             if (isError) {
-                mFutureErrorSet.add(frameNumber);
+                mFutureErrorMap.put(frameNumber, isReprocess);
             } else {
-                /**
-                 * HAL cannot send an OnResultReceived for frame N unless it knows for
-                 * sure that all frames prior to N have either errored out or completed.
-                 * So if the current frame is not an error, then all previous frames
-                 * should have arrived. The following line checks whether this holds.
-                 */
-                if (frameNumber != mCompletedFrameNumber + 1) {
-                    Log.e(TAG, String.format(
-                            "result frame number %d comes out of order, should be %d + 1",
-                            frameNumber, mCompletedFrameNumber));
-                    // Continue on to set the completed frame number to this frame anyway,
-                    // to be robust to lower-level errors and allow for clean shutdowns.
+                try {
+                    if (isReprocess) {
+                        updateCompletedReprocessFrameNumber(frameNumber);
+                    } else {
+                        updateCompletedFrameNumber(frameNumber);
+                    }
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG, e.getMessage());
                 }
-                mCompletedFrameNumber = frameNumber;
             }
             update();
         }
@@ -1049,12 +1193,13 @@
          * @param frameNumber the frame number corresponding to the result
          * @param result the total or partial result
          * @param partial {@true} if the result is partial, {@code false} if total
+         * @param isReprocess true if it is a reprocess result, false if it is a regular result.
          */
-        public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
-
+        public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
+                boolean isReprocess) {
             if (!partial) {
                 // Update the total result's frame status as being successful
-                updateTracker(frameNumber, /*isError*/false);
+                updateTracker(frameNumber, /*isError*/false, isReprocess);
                 // Don't keep a list of total results, we don't need to track them
                 return;
             }
@@ -1093,28 +1238,112 @@
             return mCompletedFrameNumber;
         }
 
+        public long getCompletedReprocessFrameNumber() {
+            return mCompletedReprocessFrameNumber;
+        }
+
+        /**
+         * Update the completed frame number for regular results.
+         *
+         * It validates that all previous frames have arrived except for reprocess frames.
+         *
+         * If there is a gap since previous regular frame number, assume the frames in the gap are
+         * reprocess frames and store them in the skipped reprocess frame number queue to check
+         * against when reprocess frames arrive.
+         */
+        private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException {
+            if (frameNumber <= mCompletedFrameNumber) {
+                throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
+            } else if (frameNumber <= mCompletedReprocessFrameNumber) {
+                // if frame number is smaller than completed reprocess frame number,
+                // it must be the head of mSkippedRegularFrameNumbers
+                if (mSkippedRegularFrameNumbers.isEmpty() == true ||
+                        frameNumber < mSkippedRegularFrameNumbers.element()) {
+                    throw new IllegalArgumentException("frame number " + frameNumber +
+                            " is a repeat");
+                } else if (frameNumber > mSkippedRegularFrameNumbers.element()) {
+                    throw new IllegalArgumentException("frame number " + frameNumber +
+                            " comes out of order. Expecting " +
+                            mSkippedRegularFrameNumbers.element());
+                }
+                // frame number matches the head of the skipped frame number queue.
+                mSkippedRegularFrameNumbers.remove();
+            } else {
+                // there is a gap of unseen frame numbers which should belong to reprocess result
+                // put all the skipped frame numbers in the queue
+                for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
+                        i < frameNumber; i++) {
+                    mSkippedReprocessFrameNumbers.add(i);
+                }
+            }
+
+            mCompletedFrameNumber = frameNumber;
+        }
+
+        /**
+         * Update the completed frame number for reprocess results.
+         *
+         * It validates that all previous frames have arrived except for regular frames.
+         *
+         * If there is a gap since previous reprocess frame number, assume the frames in the gap are
+         * regular frames and store them in the skipped regular frame number queue to check
+         * against when regular frames arrive.
+         */
+        private void updateCompletedReprocessFrameNumber(long frameNumber)
+                throws IllegalArgumentException {
+            if (frameNumber < mCompletedReprocessFrameNumber) {
+                throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
+            } else if (frameNumber < mCompletedFrameNumber) {
+                // if reprocess frame number is smaller than completed regular frame number,
+                // it must be the head of the skipped reprocess frame number queue.
+                if (mSkippedReprocessFrameNumbers.isEmpty() == true ||
+                        frameNumber < mSkippedReprocessFrameNumbers.element()) {
+                    throw new IllegalArgumentException("frame number " + frameNumber +
+                            " is a repeat");
+                } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) {
+                    throw new IllegalArgumentException("frame number " + frameNumber +
+                            " comes out of order. Expecting " +
+                            mSkippedReprocessFrameNumbers.element());
+                }
+                // frame number matches the head of the skipped frame number queue.
+                mSkippedReprocessFrameNumbers.remove();
+            } else {
+                // put all the skipped frame numbers in the queue
+                for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
+                        i < frameNumber; i++) {
+                    mSkippedRegularFrameNumbers.add(i);
+                }
+            }
+            mCompletedReprocessFrameNumber = frameNumber;
+        }
     }
 
     private void checkAndFireSequenceComplete() {
         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
+        long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
+        boolean isReprocess = false;
         Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
         while (iter.hasNext()) {
             final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
-            if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
+            boolean sequenceCompleted = false;
+            final int requestId = frameNumberRequestPair.getValue();
+            final CaptureCallbackHolder holder;
+            synchronized(mInterfaceLock) {
+                if (mRemoteDevice == null) {
+                    Log.w(TAG, "Camera closed while checking sequences");
+                    return;
+                }
 
-                // remove request from mCaptureCallbackMap
-                final int requestId = frameNumberRequestPair.getValue();
-                final CaptureCallbackHolder holder;
-                synchronized(mInterfaceLock) {
-                    if (mRemoteDevice == null) {
-                        Log.w(TAG, "Camera closed while checking sequences");
-                        return;
-                    }
-
-                    int index = mCaptureCallbackMap.indexOfKey(requestId);
-                    holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index)
-                            : null;
-                    if (holder != null) {
+                int index = mCaptureCallbackMap.indexOfKey(requestId);
+                holder = (index >= 0) ?
+                        mCaptureCallbackMap.valueAt(index) : null;
+                if (holder != null) {
+                    isReprocess = holder.getRequest().isReprocess();
+                    // check if it's okay to remove request from mCaptureCallbackMap
+                    if ((isReprocess && frameNumberRequestPair.getKey() <=
+                            completedReprocessFrameNumber) || (!isReprocess &&
+                            frameNumberRequestPair.getKey() <= completedFrameNumber)) {
+                        sequenceCompleted = true;
                         mCaptureCallbackMap.removeAt(index);
                         if (DEBUG) {
                             Log.v(TAG, String.format(
@@ -1125,36 +1354,40 @@
                         }
                     }
                 }
+            }
+
+            // If no callback is registered for this requestId or sequence completed, remove it
+            // from the frame number->request pair because it's not needed anymore.
+            if (holder == null || sequenceCompleted) {
                 iter.remove();
+            }
 
-                // Call onCaptureSequenceCompleted
-                if (holder != null) {
-                    Runnable resultDispatch = new Runnable() {
-                        @Override
-                        public void run() {
-                            if (!CameraDeviceImpl.this.isClosed()){
-                                if (DEBUG) {
-                                    Log.d(TAG, String.format(
-                                            "fire sequence complete for request %d",
-                                            requestId));
-                                }
-
-                                long lastFrameNumber = frameNumberRequestPair.getKey();
-                                if (lastFrameNumber < Integer.MIN_VALUE
-                                        || lastFrameNumber > Integer.MAX_VALUE) {
-                                    throw new AssertionError(lastFrameNumber
-                                            + " cannot be cast to int");
-                                }
-                                holder.getCallback().onCaptureSequenceCompleted(
-                                    CameraDeviceImpl.this,
-                                    requestId,
-                                    lastFrameNumber);
+            // Call onCaptureSequenceCompleted
+            if (sequenceCompleted) {
+                Runnable resultDispatch = new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!CameraDeviceImpl.this.isClosed()){
+                            if (DEBUG) {
+                                Log.d(TAG, String.format(
+                                        "fire sequence complete for request %d",
+                                        requestId));
                             }
-                        }
-                    };
-                    holder.getHandler().post(resultDispatch);
-                }
 
+                            long lastFrameNumber = frameNumberRequestPair.getKey();
+                            if (lastFrameNumber < Integer.MIN_VALUE
+                                    || lastFrameNumber > Integer.MAX_VALUE) {
+                                throw new AssertionError(lastFrameNumber
+                                        + " cannot be cast to int");
+                            }
+                            holder.getCallback().onCaptureSequenceCompleted(
+                                CameraDeviceImpl.this,
+                                requestId,
+                                lastFrameNumber);
+                        }
+                    }
+                };
+                holder.getHandler().post(resultDispatch);
             }
         }
     }
@@ -1319,9 +1552,11 @@
 
                 final CaptureCallbackHolder holder =
                         CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
+                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
 
                 boolean isPartialResult =
                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
+                boolean isReprocess = request.isReprocess();
 
                 // Check if we have a callback for this
                 if (holder == null) {
@@ -1331,7 +1566,8 @@
                                         + frameNumber);
                     }
 
-                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
+                            isReprocess);
 
                     return;
                 }
@@ -1343,11 +1579,11 @@
                                         + frameNumber);
                     }
 
-                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
+                            isReprocess);
                     return;
                 }
 
-                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
 
                 Runnable resultDispatch = null;
 
@@ -1398,7 +1634,7 @@
                 holder.getHandler().post(resultDispatch);
 
                 // Collect the partials for a total result; or mark the frame as totally completed
-                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
+                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
 
                 // Fire onCaptureSequenceCompleted
                 if (!isPartialResult) {
@@ -1460,7 +1696,7 @@
             if (DEBUG) {
                 Log.v(TAG, String.format("got error frame %d", frameNumber));
             }
-            mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
+            mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
             checkAndFireSequenceComplete();
         }
 
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 70f3463..4cd9414 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -530,6 +530,18 @@
     }
 
     @Override
+    public int createInputStream(int width, int height, int format) {
+        Log.e(TAG, "creating input stream is not supported on legacy devices");
+        return CameraBinderDecorator.INVALID_OPERATION;
+    }
+
+    @Override
+    public int getInputSurface(/*out*/ Surface surface) {
+        Log.e(TAG, "getting input surface is not supported on legacy devices");
+        return CameraBinderDecorator.INVALID_OPERATION;
+    }
+
+    @Override
     public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) {
         if (DEBUG) {
             Log.d(TAG, "createDefaultRequest called.");
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
new file mode 100644
index 0000000..dea1c5c
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+/**
+ * Immutable class to store an input configuration that is used to create a reprocessible capture
+ * session.
+ *
+ * @see CameraDevice#createReprocessibleCaptureSession
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ */
+public final class InputConfiguration {
+
+    private final int mWidth;
+    private final int mHeight;
+    private final int mFormat;
+
+    /**
+     * Create an input configration with the width, height, and user-defined format.
+     *
+     * <p>Images of an user-defined format are accessible by applications. Use
+     * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
+     * to query supported input formats</p>
+     *
+     * @param width Width of the input buffers.
+     * @param height Height of the input buffers.
+     * @param format Format of the input buffers. One of ImageFormat or PixelFormat constants.
+     *
+     * @see android.graphics.ImageFormat
+     * @see android.graphics.PixelFormat
+     * @see android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public InputConfiguration(int width, int height, int format) {
+        mWidth = width;
+        mHeight = height;
+        mFormat = format;
+    }
+
+    /**
+     * Get the width of this input configration.
+     *
+     * @return width of this input configuration.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Get the height of this input configration.
+     *
+     * @return height of this input configuration.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Get the format of this input configration.
+     *
+     * @return format of this input configuration.
+     */
+    public int getFormat() {
+        return mFormat;
+    }
+
+    /**
+     * Check if this InputConfiguration is equal to another InputConfiguration.
+     *
+     * <p>Two input configurations are equal if and only if they have the same widths, heights, and
+     * formats.</p>
+     *
+     * @param obj the object to compare this instance with.
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof InputConfiguration)) {
+            return false;
+        }
+
+        InputConfiguration otherInputConfig = (InputConfiguration) obj;
+
+        if (otherInputConfig.getWidth() == mWidth &&
+                otherInputConfig.getHeight() == mHeight &&
+                otherInputConfig.getFormat() == mFormat) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat);
+    }
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 6b4bc1e..5f85e7d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -380,6 +380,26 @@
      */
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             @NonNull AuthenticationCallback callback, int flags) {
+        authenticate(crypto, cancel, callback, flags, UserHandle.myUserId());
+    }
+
+    /**
+     * Request authentication of a crypto object. This call warms up the fingerprint hardware
+     * and starts scanning for a fingerprint. It terminates when
+     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param crypto object associated with the call or null if none required.
+     * @param cancel an object that can be used to cancel authentication
+     * @param callback an object to receive authentication events
+     * @param flags optional flags; should be 0
+     * @param userId the userId the fingerprint belongs to
+     * @hide
+     */
+    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+            @NonNull AuthenticationCallback callback, int flags, int userId) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -397,7 +417,7 @@
             mAuthenticationCallback = callback;
             mCryptoObject = crypto;
             long sessionId = crypto != null ? crypto.getOpId() : 0;
-            mService.authenticate(mToken, sessionId, getCurrentUserId(), mServiceReceiver, flags);
+            mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags);
         } catch (RemoteException e) {
             Log.w(TAG, "Remote exception while authenticating: ", e);
             if (callback != null) {
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 5d40e94..b34c9fb 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -41,6 +41,7 @@
 public final class GeofenceHardwareImpl {
     private static final String TAG = "GeofenceHardwareImpl";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final int FIRST_VERSION_WITH_CAPABILITIES = 2;
 
     private final Context mContext;
     private static GeofenceHardwareImpl sInstance;
@@ -54,6 +55,7 @@
     private IFusedGeofenceHardware mFusedService;
     private IGpsGeofenceHardware mGpsService;
     private int mCapabilities;
+    private int mVersion = 1;
 
     private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
 
@@ -145,8 +147,10 @@
     private void updateFusedHardwareAvailability() {
         boolean fusedSupported;
         try {
+            final boolean hasGnnsCapabilities = (mVersion < FIRST_VERSION_WITH_CAPABILITIES)
+                    || (mCapabilities & CAPABILITY_GNSS) != 0;
             fusedSupported = (mFusedService != null
-                    ? mFusedService.isSupported() && (mCapabilities & CAPABILITY_GNSS) != 0
+                    ? mFusedService.isSupported() && hasGnnsCapabilities
                     : false);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling LocationManagerService");
@@ -177,6 +181,11 @@
         updateFusedHardwareAvailability();
     }
 
+    public void setVersion(int version) {
+        mVersion = version;
+        updateFusedHardwareAvailability();
+    }
+
     public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
         if(mFusedService == null) {
             mFusedService = service;
diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl
index 3de766a..2ea4d23 100644
--- a/core/java/android/hardware/location/IFusedLocationHardware.aidl
+++ b/core/java/android/hardware/location/IFusedLocationHardware.aidl
@@ -121,4 +121,9 @@
      * of the locations returned in this call.
      */
     void flushBatchedLocations() = 11;
+
+    /**
+     * Returns the version of this FLP HAL implementation.
+     */
+    int getVersion() = 12;
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c7edb1a..7c5ddee 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1135,8 +1135,10 @@
         public static final int EVENT_PACKAGE_INSTALLED = 0x000c;
         // Event for a package being uninstalled.
         public static final int EVENT_PACKAGE_UNINSTALLED = 0x000d;
+        // Event for a package being uninstalled.
+        public static final int EVENT_ALARM = 0x000e;
         // Number of event types.
-        public static final int EVENT_COUNT = 0x000e;
+        public static final int EVENT_COUNT = 0x000f;
         // Mask to extract out only the type part of the event.
         public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
@@ -1158,6 +1160,8 @@
                 EVENT_USER_FOREGROUND | EVENT_FLAG_START;
         public static final int EVENT_USER_FOREGROUND_FINISH =
                 EVENT_USER_FOREGROUND | EVENT_FLAG_FINISH;
+        public static final int EVENT_ALARM_START = EVENT_ALARM | EVENT_FLAG_START;
+        public static final int EVENT_ALARM_FINISH = EVENT_ALARM | EVENT_FLAG_FINISH;
 
         // For CMD_EVENT.
         public int eventCode;
@@ -1789,12 +1793,12 @@
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
-            "motion", "active", "pkginst", "pkgunin"
+            "motion", "active", "pkginst", "pkgunin", "alarm"
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
             "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
-            "Esm", "Eac", "Epi", "Epu"
+            "Esm", "Eac", "Epi", "Epu", "Eal"
     };
 
     /**
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 512e212..360e5c68 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -34,6 +34,7 @@
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
 
 import org.apache.harmony.dalvik.ddmc.Chunk;
 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
@@ -1101,6 +1102,95 @@
     }
 
     /**
+     * Returns the value of a particular runtime statistic or {@code null} if no
+     * such runtime statistic exists.
+     *
+     * <p>The following table lists the runtime statistics that the runtime supports.
+     * Note runtime statistics may be added or removed in a future API level.</p>
+     *
+     * <table>
+     *     <thead>
+     *         <tr>
+     *             <th>Runtime statistic name</th>
+     *             <th>Meaning</th>
+     *             <th>Example</th>
+     *             <th>Supported (API Levels)</th>
+     *         </tr>
+     *     </thead>
+     *     <tbody>
+     *         <tr>
+     *             <td>art.gc.gc-count</td>
+     *             <td>The number of garbage collection runs.</td>
+     *             <td>{@code 164}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.gc-time</td>
+     *             <td>The total duration of garbage collection runs in ms.</td>
+     *             <td>{@code 62364}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.bytes-allocated</td>
+     *             <td>The total number of bytes that the application allocated.</td>
+     *             <td>{@code 1463948408}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.bytes-freed</td>
+     *             <td>The total number of bytes that garbage collection reclaimed.</td>
+     *             <td>{@code 1313493084}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.blocking-gc-count</td>
+     *             <td>The number of blocking garbage collection runs.</td>
+     *             <td>{@code 2}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.blocking-gc-time</td>
+     *             <td>The total duration of blocking garbage collection runs in ms.</td>
+     *             <td>{@code 804}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.gc-count-rate-histogram</td>
+     *             <td>The histogram of the number of garbage collection runs per 10 seconds.</td>
+     *             <td>{@code 0:34503,1:45350,2:11281,3:8088,4:43,5:8}</td>
+     *             <td>23</td>
+     *         </tr>
+     *         <tr>
+     *             <td>art.gc.blocking-gc-count-rate-histogram</td>
+     *             <td>The histogram of the number of garbage collection runs per 10 seconds.</td>
+     *             <td>{@code 0:99269,1:1,2:1}</td>
+     *             <td>23</td>
+     *         </tr>
+     *     </tbody>
+     * </table>
+     *
+     * @param statName
+     *            the name of the runtime statistic to look up.
+     * @return the value of the specified runtime statistic or {@code null} if the
+     *         runtime statistic doesn't exist.
+     * @hide
+     */
+    public static String getRuntimeStat(String statName) {
+        return VMDebug.getRuntimeStat(statName);
+    }
+
+    /**
+     * Returns a map of the names/values of the runtime statistics
+     * that {@link #getRuntimeStat()} supports.
+     *
+     * @return a map of the names/values of the supported runtime statistics.
+     * @hide
+     */
+    public static Map<String, String> getRuntimeStats() {
+        return VMDebug.getRuntimeStats();
+    }
+
+    /**
      * Returns the size of the native heap.
      * @return The size of the native heap in bytes.
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9edf6ad..109c23b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7717,6 +7717,13 @@
          */
         public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1",
                 "user_preferred_sub2","user_preferred_sub3"};
+
+        /**
+         * Whether to enable new contacts aggregator or not.
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator";
     }
 
     /**
diff --git a/core/java/android/service/gatekeeper/IGateKeeperService.aidl b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
new file mode 100644
index 0000000..2f3e296
--- /dev/null
+++ b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.gatekeeper;
+
+/**
+ * Interface for communication with GateKeeper, the
+ * secure password storage daemon.
+ *
+ * This must be kept manually in sync with system/core/gatekeeperd
+ * until AIDL can generate both C++ and Java bindings.
+ *
+ * @hide
+ */
+interface IGateKeeperService {
+    /**
+     * Enrolls a password, returning the handle to the enrollment to be stored locally.
+     * @param uid The Android user ID associated to this enrollment
+     * @param currentPasswordHandle The previously enrolled handle, or null if none
+     * @param currentPassword The previously enrolled plaintext password, or null if none.
+     *                        If provided, must verify against the currentPasswordHandle.
+     * @param desiredPassword The new desired password, for which a handle will be returned
+     *                        upon success.
+     * @return the handle corresponding to desiredPassword, or null
+     */
+    byte[] enroll(int uid, in byte[] currentPasswordHandle, in byte[] currentPassword,
+            in byte[] desiredPassword);
+
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return True if the authentication was successful
+     */
+    boolean verify(int uid, in byte[] enrolledPasswordHandle,
+            in byte[] providedPassword);
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param challenge a challenge to authenticate agaisnt the device credential. If successful
+     *                  authentication occurs, this value will be written to the returned 
+     *                  authentication attestation.
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return an opaque attestation of authentication on success, or null.
+     */
+    byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle, 
+            in byte[] providedPassword);
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 20d7079..71b7f76 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,8 +20,10 @@
 import android.app.Dialog;
 import android.app.Instrumentation;
 import android.app.VoiceInteractor;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
@@ -65,7 +67,8 @@
  * when done.  It can also initiate voice interactions with applications by calling
  * {@link #startVoiceActivity}</p>.
  */
-public abstract class VoiceInteractionSession implements KeyEvent.Callback {
+public abstract class VoiceInteractionSession implements KeyEvent.Callback,
+        ComponentCallbacks2 {
     static final String TAG = "VoiceInteractionSession";
     static final boolean DEBUG = true;
 
@@ -855,6 +858,18 @@
         hide();
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+    }
+
+    @Override
+    public void onLowMemory() {
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+    }
+
     /**
      * Compute the interesting insets into your UI.  The default implementation
      * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
index 008d55f..8f988f3 100644
--- a/core/java/android/service/voice/VoiceInteractionSessionService.java
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -19,6 +19,7 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
@@ -76,6 +77,30 @@
         return mInterface.asBinder();
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mSession != null) {
+            mSession.onConfigurationChanged(newConfig);
+        }
+    }
+
+    @Override
+    public void onLowMemory() {
+        super.onLowMemory();
+        if (mSession != null) {
+            mSession.onLowMemory();
+        }
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        super.onTrimMemory(level);
+        if (mSession != null) {
+            mSession.onTrimMemory(level);
+        }
+    }
+
     void doNewSession(IBinder token, Bundle args, int startFlags) {
         if (mSession != null) {
             mSession.doDestroy();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3fa8c81..6b28746 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1795,6 +1795,8 @@
      *                          11       PFLAG2_TEXT_DIRECTION_FLAGS[3]
      *                         1         PFLAG2_TEXT_DIRECTION_FLAGS[4]
      *                         1 1       PFLAG2_TEXT_DIRECTION_FLAGS[5]
+     *                         11        PFLAG2_TEXT_DIRECTION_FLAGS[6]
+     *                         111       PFLAG2_TEXT_DIRECTION_FLAGS[7]
      *                         111       PFLAG2_TEXT_DIRECTION_MASK
      *                        1          PFLAG2_TEXT_DIRECTION_RESOLVED
      *                       1           PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT
@@ -1968,6 +1970,20 @@
     public static final int TEXT_DIRECTION_LOCALE = 5;
 
     /**
+     * Text direction is using "first strong algorithm". The first strong directional character
+     * determines the paragraph direction. If there is no strong directional character, the
+     * paragraph direction is LTR.
+     */
+    public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6;
+
+    /**
+     * Text direction is using "first strong algorithm". The first strong directional character
+     * determines the paragraph direction. If there is no strong directional character, the
+     * paragraph direction is RTL.
+     */
+    public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7;
+
+    /**
      * Default text direction is inherited
      */
     private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
@@ -2002,7 +2018,9 @@
             TEXT_DIRECTION_ANY_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
             TEXT_DIRECTION_LTR << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
             TEXT_DIRECTION_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
-            TEXT_DIRECTION_LOCALE << PFLAG2_TEXT_DIRECTION_MASK_SHIFT
+            TEXT_DIRECTION_LOCALE << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
+            TEXT_DIRECTION_FIRST_STRONG_LTR << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
+            TEXT_DIRECTION_FIRST_STRONG_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT
     };
 
     /**
@@ -19636,11 +19654,13 @@
      * @return the defined text direction. It can be one of:
      *
      * {@link #TEXT_DIRECTION_INHERIT},
-     * {@link #TEXT_DIRECTION_FIRST_STRONG}
+     * {@link #TEXT_DIRECTION_FIRST_STRONG},
      * {@link #TEXT_DIRECTION_ANY_RTL},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
-     * {@link #TEXT_DIRECTION_LOCALE}
+     * {@link #TEXT_DIRECTION_LOCALE},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL}
      *
      * @attr ref android.R.styleable#View_textDirection
      *
@@ -19652,7 +19672,9 @@
             @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
-            @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
     })
     public int getRawTextDirection() {
         return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_MASK) >> PFLAG2_TEXT_DIRECTION_MASK_SHIFT;
@@ -19664,11 +19686,13 @@
      * @param textDirection the direction to set. Should be one of:
      *
      * {@link #TEXT_DIRECTION_INHERIT},
-     * {@link #TEXT_DIRECTION_FIRST_STRONG}
+     * {@link #TEXT_DIRECTION_FIRST_STRONG},
      * {@link #TEXT_DIRECTION_ANY_RTL},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE}
+     * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL},
      *
      * Resolution will be done if the value is set to TEXT_DIRECTION_INHERIT. The resolution
      * proceeds up the parent chain of the view to get the value. If there is no parent, then it will
@@ -19698,11 +19722,13 @@
      *
      * @return the resolved text direction. Returns one of:
      *
-     * {@link #TEXT_DIRECTION_FIRST_STRONG}
+     * {@link #TEXT_DIRECTION_FIRST_STRONG},
      * {@link #TEXT_DIRECTION_ANY_RTL},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
-     * {@link #TEXT_DIRECTION_LOCALE}
+     * {@link #TEXT_DIRECTION_LOCALE},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL}
      *
      * @attr ref android.R.styleable#View_textDirection
      */
@@ -19712,7 +19738,9 @@
             @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
-            @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
     })
     public int getTextDirection() {
         return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
@@ -19771,6 +19799,8 @@
                         case TEXT_DIRECTION_LTR:
                         case TEXT_DIRECTION_RTL:
                         case TEXT_DIRECTION_LOCALE:
+                        case TEXT_DIRECTION_FIRST_STRONG_LTR:
+                        case TEXT_DIRECTION_FIRST_STRONG_RTL:
                             mPrivateFlags2 |=
                                     (parentResolvedDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
                             break;
@@ -19784,6 +19814,8 @@
                 case TEXT_DIRECTION_LTR:
                 case TEXT_DIRECTION_RTL:
                 case TEXT_DIRECTION_LOCALE:
+                case TEXT_DIRECTION_FIRST_STRONG_LTR:
+                case TEXT_DIRECTION_FIRST_STRONG_RTL:
                     // Resolved direction is the same as text direction
                     mPrivateFlags2 |= (textDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
                     break;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 29073be..1be05f3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3370,7 +3370,7 @@
         // Parent's (TextView) previous position in window
         private int mLastParentX, mLastParentY;
         // Previous text character offset
-        private int mPreviousOffset = -1;
+        protected int mPreviousOffset = -1;
         // Previous text character offset
         private boolean mPositionHasChanged = true;
         // Minimum touch target size for handles
@@ -3830,8 +3830,6 @@
     }
 
     private class SelectionStartHandleView extends HandleView {
-        // The previous offset this handle was at.
-        private int mPrevOffset;
         // Indicates whether the cursor is making adjustments within a word.
         private boolean mInWord = false;
         // Offset to track difference between touch and word boundary.
@@ -3879,7 +3877,7 @@
             int end = getWordEnd(offset, true);
             int start = getWordStart(offset);
 
-            if (offset < mPrevOffset) {
+            if (offset < mPreviousOffset) {
                 // User is increasing the selection.
                 if (!mInWord || currLine < mPrevLine) {
                     // We're not in a word, or we're on a different line so we'll expand by
@@ -3888,21 +3886,19 @@
                     if (offset <= end - offsetToWord || currLine < mPrevLine) {
                         offset = start;
                     } else {
-                        offset = mPrevOffset;
+                        offset = mPreviousOffset;
                     }
                 }
-                mPrevOffset = offset;
                 mTouchWordOffset = Math.max(trueOffset - offset, 0);
                 mInWord = !isStartBoundary(offset);
                 positionCursor = true;
-            } else if (offset - mTouchWordOffset > mPrevOffset) {
+            } else if (offset - mTouchWordOffset > mPreviousOffset) {
                 // User is shrinking the selection.
                 if (currLine > mPrevLine) {
                     // We're on a different line, so we'll snap to word boundaries.
                     offset = end;
                 }
                 offset -= mTouchWordOffset;
-                mPrevOffset = offset;
                 mInWord = !isEndBoundary(offset);
                 positionCursor = true;
             }
@@ -3936,8 +3932,6 @@
     }
 
     private class SelectionEndHandleView extends HandleView {
-        // The previous offset this handle was at.
-        private int mPrevOffset;
         // Indicates whether the cursor is making adjustments within a word.
         private boolean mInWord = false;
         // Offset to track difference between touch and word boundary.
@@ -3986,7 +3980,7 @@
             int end = getWordEnd(offset, true);
             int start = getWordStart(offset);
 
-            if (offset > mPrevOffset) {
+            if (offset > mPreviousOffset) {
                 // User is increasing the selection.
                 if (!mInWord || currLine > mPrevLine) {
                     // We're not in a word, or we're on a different line so we'll expand by
@@ -3995,21 +3989,19 @@
                     if (offset >= start + midPoint || currLine > mPrevLine) {
                         offset = end;
                     } else {
-                        offset = mPrevOffset;
+                        offset = mPreviousOffset;
                     }
                 }
-                mPrevOffset = offset;
                 mTouchWordOffset = Math.max(offset - trueOffset, 0);
                 mInWord = !isEndBoundary(offset);
                 positionCursor = true;
-            } else if (offset + mTouchWordOffset < mPrevOffset) {
+            } else if (offset + mTouchWordOffset < mPreviousOffset) {
                 // User is shrinking the selection.
                 if (currLine > mPrevLine) {
                     // We're on a different line, so we'll snap to word boundaries.
                     offset = getWordStart(offset);
                 }
                 offset += mTouchWordOffset;
-                mPrevOffset = offset;
                 positionCursor = true;
                 mInWord = !isStartBoundary(offset);
             }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9bbf375..b44a886 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9275,6 +9275,10 @@
                 return TextDirectionHeuristics.RTL;
             case TEXT_DIRECTION_LOCALE:
                 return TextDirectionHeuristics.LOCALE;
+            case TEXT_DIRECTION_FIRST_STRONG_LTR:
+                return TextDirectionHeuristics.FIRSTSTRONG_LTR;
+            case TEXT_DIRECTION_FIRST_STRONG_RTL:
+                return TextDirectionHeuristics.FIRSTSTRONG_RTL;
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fbb2dfc..87605f6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2692,6 +2692,32 @@
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_FINISH, name, uid);
     }
 
+    public void noteAlarmStartLocked(String name, int uid) {
+        if (!mRecordAllHistory) {
+            return;
+        }
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_ALARM_START, name, uid, 0)) {
+            return;
+        }
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ALARM_START, name, uid);
+    }
+
+    public void noteAlarmFinishLocked(String name, int uid) {
+        if (!mRecordAllHistory) {
+            return;
+        }
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_ALARM_FINISH, name, uid, 0)) {
+            return;
+        }
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ALARM_FINISH, name, uid);
+    }
+
     private void requestWakelockCpuUpdate() {
         if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
             Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
@@ -2709,6 +2735,7 @@
         if (!enabled) {
             // Clear out any existing state.
             mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK);
+            mActiveEvents.removeEvents(HistoryItem.EVENT_ALARM);
             // Record the currently running processes as stopping, now that we are no
             // longer tracking them.
             HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 0cb1f38..bfafff6 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -24,10 +24,12 @@
     boolean getBoolean(in String key, in boolean defaultValue, in int userId);
     long getLong(in String key, in long defaultValue, in int userId);
     String getString(in String key, in String defaultValue, in int userId);
-    void setLockPattern(in String pattern, int userId);
+    void setLockPattern(in String pattern, in String savedPattern, int userId);
     boolean checkPattern(in String pattern, int userId);
-    void setLockPassword(in String password, int userId);
+    byte[] verifyPattern(in String pattern, long challenge, int userId);
+    void setLockPassword(in String password, in String savedPassword, int userId);
     boolean checkPassword(in String password, int userId);
+    byte[] verifyPassword(in String password, long challenge, int userId);
     boolean checkVoldPassword(int userId);
     boolean havePattern(int userId);
     boolean havePassword(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 2967876..123d1ac 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -280,6 +280,24 @@
     }
 
     /**
+     * Check to see if a pattern matches the saved pattern.
+     * If pattern matches, return an opaque attestation that the challenge
+     * was verified.
+     *
+     * @param pattern The pattern to check.
+     * @param challenge The challenge to verify against the pattern
+     * @return the attestation that the challenge was verified, or null.
+     */
+    public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge) {
+        final int userId = getCurrentOrCallingUserId();
+        try {
+            return getLockSettings().verifyPattern(patternToString(pattern), challenge, userId);
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
+    /**
      * Check to see if a pattern matches the saved pattern.  If no pattern exists,
      * always returns true.
      * @param pattern The pattern to check.
@@ -295,6 +313,24 @@
     }
 
     /**
+     * Check to see if a password matches the saved password.
+     * If password matches, return an opaque attestation that the challenge
+     * was verified.
+     *
+     * @param password The password to check.
+     * @param challenge The challenge to verify against the password
+     * @return the attestation that the challenge was verified, or null.
+     */
+    public byte[] verifyPassword(String password, long challenge) {
+        final int userId = getCurrentOrCallingUserId();
+        try {
+            return getLockSettings().verifyPassword(password, challenge, userId);
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
+    /**
      * Check to see if a password matches the saved password.  If no password exists,
      * always returns true.
      * @param password The password to check.
@@ -425,8 +461,8 @@
         setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
 
         try {
-            getLockSettings().setLockPassword(null, userHandle);
-            getLockSettings().setLockPattern(null, userHandle);
+            getLockSettings().setLockPassword(null, null, userHandle);
+            getLockSettings().setLockPattern(null, null, userHandle);
         } catch (RemoteException e) {
             // well, we tried...
         }
@@ -477,24 +513,30 @@
     /**
      * Save a lock pattern.
      * @param pattern The new pattern to save.
+     * @param savedPattern The previously saved pattern, or null if none
      */
-    public void saveLockPattern(List<LockPatternView.Cell> pattern) {
-        this.saveLockPattern(pattern, getCurrentOrCallingUserId());
+    public void saveLockPattern(List<LockPatternView.Cell> pattern,
+            String savedPattern) {
+        this.saveLockPattern(pattern, savedPattern, getCurrentOrCallingUserId());
     }
 
+    public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) {
+        this.saveLockPattern(pattern, null, userId);
+    }
     /**
      * Save a lock pattern.
      * @param pattern The new pattern to save.
+     * @param savedPattern The previously saved pattern, converted to String format
      * @param userId the user whose pattern is to be saved.
      */
-    public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) {
+    public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
         try {
             if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
                 throw new IllegalArgumentException("pattern must not be null and at least "
                         + MIN_LOCK_PATTERN_SIZE + " dots long.");
             }
 
-            getLockSettings().setLockPattern(patternToString(pattern), userId);
+            getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId);
             DevicePolicyManager dpm = getDevicePolicyManager();
 
             // Update the device encryption password.
@@ -685,10 +727,11 @@
      * as the requested mode, but will adjust the mode to be as good as the
      * pattern.
      * @param password The password to save
+     * @param savedPassword The previously saved lock password, or null if none
      * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
      */
-    public void saveLockPassword(String password, int quality) {
-        saveLockPassword(password, quality, getCurrentOrCallingUserId());
+    public void saveLockPassword(String password, String savedPassword, int quality) {
+        saveLockPassword(password, savedPassword, quality, getCurrentOrCallingUserId());
     }
 
     /**
@@ -699,7 +742,8 @@
      * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
      * @param userHandle The userId of the user to change the password for
      */
-    public void saveLockPassword(String password, int quality, int userHandle) {
+    public void saveLockPassword(String password, String savedPassword, int quality,
+            int userHandle) {
         try {
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
@@ -707,7 +751,7 @@
                         + "of length " + MIN_LOCK_PASSWORD_SIZE);
             }
 
-            getLockSettings().setLockPassword(password, userHandle);
+            getLockSettings().setLockPassword(password, savedPassword, userHandle);
             int computedQuality = computePasswordQuality(password);
 
             // Update the device encryption password.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3d22e52..6b44cd4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1584,6 +1584,24 @@
     <permission android:name="android.permission.WRITE_APN_SETTINGS"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to allow access the subscribed feeds ContentProvider.
+         @hide
+         @removed
+     -->
+    <permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
+        android:label="@string/permlab_subscribedFeedsRead"
+        android:description="@string/permdesc_subscribedFeedsRead"
+        android:protectionLevel="normal" />
+
+    <!--
+        @hide
+        @removed
+    -->
+    <permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE"
+        android:label="@string/permlab_subscribedFeedsWrite"
+        android:description="@string/permdesc_subscribedFeedsWrite"
+        android:protectionLevel="dangerous" />
+
     <!-- Allows applications to change network connectivity state -->
     <permission android:name="android.permission.CHANGE_NETWORK_STATE"
         android:description="@string/permdesc_changeNetworkState"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 04e40a7..a623d0c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2520,6 +2520,12 @@
             <enum name="rtl" value="4" />
             <!-- The paragraph direction is coming from the system Locale. -->
             <enum name="locale" value="5" />
+            <!-- The first strong directional character determines the paragraph direction. If
+                 there is no strong directional character, the paragraph direction is LTR. -->
+            <enum name="firstStrongLtr" value="6" />
+            <!-- The first strong directional character determines the paragraph direction. If
+                 there is no strong directional character, the paragraph direction is RTL. -->
+            <enum name="firstStrongRtl" value="7" />
         </attr>
 
         <!-- Defines the alignment of the text. A heuristic is used to determine the resolved
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2dbba8e..87c50e8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1279,6 +1279,12 @@
     <string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_subscribedFeedsWrite">write subscribed feeds</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_subscribedFeedsWrite">Allows the app to modify
+        your currently synced feeds. Malicious apps may change your synced feeds.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readDictionary">read terms you added to the dictionary</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readDictionary">Allows the app to read all words,
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 5c3b90b..dc364a12 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -252,6 +252,11 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansBamum-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansBatak-Regular.ttf</file>
         </fileset>
     </family>
@@ -312,11 +317,31 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansLisu-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansMandaic-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansMeeteiMayek-Regular.ttf</file>
         </fileset>
     </family>
     <family>
         <fileset>
+            <file>NotoSansNewTaiLue-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansNKo-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansOlChiki-Regular.ttf</file>
         </fileset>
     </family>
@@ -342,6 +367,11 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansSyriacEstrangela-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansTagbanwa-Regular.ttf</file>
         </fileset>
     </family>
@@ -362,6 +392,11 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansVai-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>NotoSansYi-Regular.ttf</file>
         </fileset>
     </family>
@@ -411,7 +446,7 @@
         </fileset>
     </family>
     <!--
-        Noto Sans Tai Le is intentionally kept last, to make sure it doesn't override
+        Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
     -->
     <family>
@@ -419,4 +454,9 @@
             <file>NotoSansTaiLe-Regular.ttf</file>
         </fileset>
     </family>
+    <family>
+        <fileset>
+            <file>NotoSansMongolian-Regular.ttf</file>
+        </fileset>
+    </family>
 </familyset>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index f3a7647..44ea1c9 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -239,6 +239,9 @@
         <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
     </family>
     <family>
@@ -275,9 +278,21 @@
         <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
     </family>
     <family>
@@ -293,6 +308,9 @@
         <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
     </family>
     <family>
@@ -305,6 +323,9 @@
         <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
     </family>
     <family lang="zh-Hans">
@@ -335,10 +356,13 @@
         <font weight="400" style="normal">MTLmr3m.ttf</font>
     </family>
     <!--
-        Noto Sans Tai Le is intentionally kept last, to make sure it doesn't override
+        Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
     -->
     <family>
         <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
     </family>
+    <family>
+        <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+    </family>
 </familyset>
diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java
index 3826679..a3e5aec 100644
--- a/keystore/java/android/security/EcIesParameterSpec.java
+++ b/keystore/java/android/security/EcIesParameterSpec.java
@@ -83,11 +83,10 @@
     }
 
     /**
-     * Default parameter spec: NIST P-256 curve (aka secp256r1 aka prime256v1), compressed point
-     * format, {@code HKDFwithSHA256}, DEM uses 128-bit AES GCM.
+     * Default parameter spec: compressed point format, {@code HKDFwithSHA256}, DEM uses 128-bit AES
+     * GCM.
      */
     public static final EcIesParameterSpec DEFAULT = new EcIesParameterSpec(
-            "P-256",
             PointFormat.COMPRESSED,
             "HKDFwithSHA256",
             "AES/GCM/NoPadding",
@@ -95,7 +94,6 @@
             null,
             0);
 
-    private final String mKemCurveName;
     private final @PointFormatEnum int mKemPointFormat;
     private final String mKemKdfAlgorithm;
     private final String mDemCipherTransformation;
@@ -104,14 +102,12 @@
     private final int mDemMacKeySize;
 
     private EcIesParameterSpec(
-            String kemCurveName,
             @PointFormatEnum int kemPointFormat,
             String kemKdfAlgorithm,
             String demCipherTransformation,
             int demCipherKeySize,
             String demMacAlgorithm,
             int demMacKeySize) {
-        mKemCurveName = kemCurveName;
         mKemPointFormat = kemPointFormat;
         mKemKdfAlgorithm = kemKdfAlgorithm;
         mDemCipherTransformation = demCipherTransformation;
@@ -121,15 +117,6 @@
     }
 
     /**
-     * Returns KEM EC curve name (e.g., {@code secp256r1}) or {@code null} if not specified.
-     *
-     * @hide
-     */
-    public String getKemCurveName() {
-        return mKemCurveName;
-    }
-
-    /**
      * Returns KEM EC point wire format or {@link PointFormat#UNSPECIFIED} if not specified.
      */
     public @PointFormatEnum int getKemPointFormat() {
@@ -188,7 +175,6 @@
      * Builder of {@link EcIesParameterSpec}.
      */
     public static class Builder {
-        private String mKemCurveName;
         private @PointFormatEnum int mKemPointFormat = PointFormat.UNSPECIFIED;
         private String mKemKdfAlgorithm;
         private String mDemCipherTransformation;
@@ -197,18 +183,6 @@
         private int mDemMacKeySize = -1;
 
         /**
-         * Sets KEM EC curve name. For example, {@code P-256} or {@code secp256r1}.
-         *
-         * <p>NOTE: Only curves with cofactor of {@code 1} are supported.
-         *
-         * @hide
-         */
-        public Builder setKemCurveName(String name) {
-            mKemCurveName = name;
-            return this;
-        }
-
-        /**
          * Sets KEM EC point wire format.
          */
         public Builder setKemPointFormat(@PointFormatEnum int pointFormat) {
@@ -276,7 +250,6 @@
         public EcIesParameterSpec build() {
             int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize;
             return new EcIesParameterSpec(
-                    mKemCurveName,
                     mKemPointFormat,
                     mKemKdfAlgorithm,
                     mDemCipherTransformation,
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index e297d26..5e5cf37 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -52,6 +52,11 @@
  */
 public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
 
+    private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+    private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+    private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
+    private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
     private final Context mContext;
 
     private final String mKeystoreAlias;
@@ -147,22 +152,29 @@
             throw new IllegalArgumentException("context == null");
         } else if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
-        } else if (subjectDN == null) {
-            throw new IllegalArgumentException("subjectDN == null");
-        } else if (serialNumber == null) {
-            throw new IllegalArgumentException("serialNumber == null");
-        } else if (startDate == null) {
-            throw new IllegalArgumentException("startDate == null");
-        } else if (endDate == null) {
-            throw new IllegalArgumentException("endDate == null");
-        } else if (endDate.before(startDate)) {
-            throw new IllegalArgumentException("endDate < startDate");
         } else if ((userAuthenticationValidityDurationSeconds < 0)
                 && (userAuthenticationValidityDurationSeconds != -1)) {
             throw new IllegalArgumentException(
                     "userAuthenticationValidityDurationSeconds must not be negative");
         }
 
+        if (subjectDN == null) {
+            subjectDN = DEFAULT_CERT_SUBJECT;
+        }
+        if (startDate == null) {
+            startDate = DEFAULT_CERT_NOT_BEFORE;
+        }
+        if (endDate == null) {
+            endDate = DEFAULT_CERT_NOT_AFTER;
+        }
+        if (serialNumber == null) {
+            serialNumber = DEFAULT_CERT_SERIAL_NUMBER;
+        }
+
+        if (endDate.before(startDate)) {
+            throw new IllegalArgumentException("endDate < startDate");
+        }
+
         mContext = context;
         mKeystoreAlias = keyStoreAlias;
         mKeyType = keyType;
@@ -556,6 +568,10 @@
         /**
          * Sets the subject used for the self-signed certificate of the
          * generated key pair.
+         *
+         * <p>The subject must be specified on API Level
+         * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
+         * newer platforms the subject defaults to {@code CN=fake} if not specified.
          */
         public Builder setSubject(X500Principal subject) {
             if (subject == null) {
@@ -568,6 +584,10 @@
         /**
          * Sets the serial number used for the self-signed certificate of the
          * generated key pair.
+         *
+         * <p>The serial number must be specified on API Level
+         * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
+         * newer platforms the serial number defaults to {@code 1} if not specified.
          */
         public Builder setSerialNumber(BigInteger serialNumber) {
             if (serialNumber == null) {
@@ -580,6 +600,10 @@
         /**
          * Sets the start of the validity period for the self-signed certificate
          * of the generated key pair.
+         *
+         * <p>The date must be specified on API Level
+         * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
+         * newer platforms the date defaults to {@code Jan 1 1970} if not specified.
          */
         public Builder setStartDate(Date startDate) {
             if (startDate == null) {
@@ -592,6 +616,10 @@
         /**
          * Sets the end of the validity period for the self-signed certificate
          * of the generated key pair.
+         *
+         * <p>The date must be specified on API Level
+         * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
+         * newer platforms the date defaults to {@code Jan 1 2048} if not specified.
          */
         public Builder setEndDate(Date endDate) {
             if (endDate == null) {
diff --git a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
index bc8dd13..681a9ff 100644
--- a/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/KeyPairGeneratorSpecTest.java
@@ -24,6 +24,11 @@
 import javax.security.auth.x500.X500Principal;
 
 public class KeyPairGeneratorSpecTest extends AndroidTestCase {
+    private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+    private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+    private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1980
+    private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
     private static final String TEST_ALIAS_1 = "test1";
 
     private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
@@ -105,46 +110,37 @@
         }
     }
 
-    public void testConstructor_NullSubjectDN_Failure() throws Exception {
-        try {
-            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW,
-                    NOW_PLUS_10_YEARS, 0);
-            fail("Should throw IllegalArgumentException when subjectDN is null");
-        } catch (IllegalArgumentException success) {
-        }
+    public void testConstructor_NullSubjectDN_Success() throws Exception {
+        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
+                getContext(), TEST_ALIAS_1, "RSA", 1024, null, null, SERIAL_1, NOW,
+                NOW_PLUS_10_YEARS, 0);
+        assertEquals(DEFAULT_CERT_SUBJECT, spec.getSubjectDN());
     }
 
-    public void testConstructor_NullSerial_Failure() throws Exception {
-        try {
-            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW,
-                    NOW_PLUS_10_YEARS, 0);
-            fail("Should throw IllegalArgumentException when startDate is null");
-        } catch (IllegalArgumentException success) {
-        }
+    public void testConstructor_NullSerial_Success() throws Exception {
+        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
+                getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, null, NOW,
+                NOW_PLUS_10_YEARS, 0);
+        assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getSerialNumber());
     }
 
-    public void testConstructor_NullStartDate_Failure() throws Exception {
-        try {
-            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
-                    null, NOW_PLUS_10_YEARS, 0);
-            fail("Should throw IllegalArgumentException when startDate is null");
-        } catch (IllegalArgumentException success) {
-        }
+    public void testConstructor_NullStartDate_Success() throws Exception {
+        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
+                getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, null,
+                NOW_PLUS_10_YEARS, 0);
+        assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getStartDate());
     }
 
-    public void testConstructor_NullEndDate_Failure() throws Exception {
-        try {
-            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
-                    NOW, null, 0);
-            fail("Should throw IllegalArgumentException when keystoreAlias is null");
-        } catch (IllegalArgumentException success) {
-        }
+    public void testConstructor_NullEndDate_Success() throws Exception {
+        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec(
+                getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1, NOW, null, 0);
+        assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getEndDate());
     }
 
     public void testConstructor_EndBeforeStart_Failure() throws Exception {
         try {
-            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1, SERIAL_1,
-                    NOW_PLUS_10_YEARS, NOW, 0);
+            new KeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, "RSA", 1024, null, TEST_DN_1,
+                    SERIAL_1, NOW_PLUS_10_YEARS, NOW, 0);
             fail("Should throw IllegalArgumentException when end is before start");
         } catch (IllegalArgumentException success) {
         }
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardware.java b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
index 480a18c..eb3b2f4 100644
--- a/location/lib/java/com/android/location/provider/FusedLocationHardware.java
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
@@ -216,6 +216,26 @@
         }
     }
 
+
+    /**
+     * Returns the version of the FLP HAL.
+     *
+     * <p>Version 1 is the initial release.
+     * <p>Version 2 adds the ability to use {@link #flushBatchedLocations},
+     * {@link FusedLocationHardwareSink#onCapabilities}, and
+     * {@link FusedLocationHardwareSink#onStatusChanged}.
+     *
+     * <p>This method is only available on API 23 or later.  Older APIs have version 1.
+     */
+    public int getVersion() {
+        try {
+            return mLocationHardware.getVersion();
+        } catch(RemoteException e) {
+            Log.e(TAG, "RemoteException at getVersion");
+        }
+        return 1;
+    }
+
     /*
      * Helper methods and classes
      */
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
index 618d5d6..01d37ac 100644
--- a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
@@ -54,7 +54,8 @@
     /**
      * Called when the status changes in the underlying FLP HAL
      * implementation (the ability to compute location).  This
-     * callback will only be made on API 23 or later.
+     * callback will only be made on version 2 or later
+     * (see {@link FusedLocationHardware#getVersion()}).
      *
      * @param status One of FLP_STATUS_LOCATION_AVAILABLE or
      *               FLP_STATUS_LOCATION_UNAVAILABLE as defined in
diff --git a/media/java/android/media/VolumePolicy.java b/media/java/android/media/VolumePolicy.java
index 2d3376a..1d33128 100644
--- a/media/java/android/media/VolumePolicy.java
+++ b/media/java/android/media/VolumePolicy.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /** @hide */
 public final class VolumePolicy implements Parcelable {
     public static final VolumePolicy DEFAULT = new VolumePolicy(false, false, true, 400);
@@ -53,6 +55,23 @@
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hash(volumeDownToEnterSilent, volumeUpToExitSilent, doNotDisturbWhenSilent,
+                vibrateToSilentDebounce);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof VolumePolicy)) return false;
+        if (o == this) return true;
+        final VolumePolicy other = (VolumePolicy) o;
+        return other.volumeDownToEnterSilent == volumeDownToEnterSilent
+                && other.volumeUpToExitSilent == volumeUpToExitSilent
+                && other.doNotDisturbWhenSilent == doNotDisturbWhenSilent
+                && other.vibrateToSilentDebounce == vibrateToSilentDebounce;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index e05e1fc..0466540 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -160,7 +160,7 @@
         assertEquals(CameraBinderTestUtils.NO_ERROR, status);
         assertFalse(metadata.isEmpty());
 
-        CaptureRequest.Builder request = new CaptureRequest.Builder(metadata);
+        CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false);
         assertFalse(request.isEmpty());
         assertFalse(metadata.isEmpty());
         if (needStream) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 147ac19..a02fb4a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.IUserSwitchObserver;
@@ -795,7 +796,8 @@
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
-            mFpm.authenticate(null, mFingerprintCancelSignal, mAuthenticationCallback, 0);
+            mFpm.authenticate(null, mFingerprintCancelSignal, mAuthenticationCallback, 0,
+                    ActivityManager.getCurrentUser());
             setFingerprintRunningDetectionRunning(true);
         }
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0b6ab99..870f043 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2066,7 +2066,7 @@
                     LockPatternUtils lpu = new LockPatternUtils(mContext);
                     List<LockPatternView.Cell> cellPattern =
                             LockPatternUtils.stringToPattern(lockPattern);
-                    lpu.saveLockPattern(cellPattern);
+                    lpu.saveLockPattern(cellPattern, null);
                 } catch (IllegalArgumentException e) {
                     // Don't want corrupted lock pattern to hang the reboot process
                 }
diff --git a/packages/SystemUI/res/drawable/ic_delete.xml b/packages/SystemUI/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..4e80ddf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_delete.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#ffffffff"
+        android:pathData="M12 38c0 2.21 1.79 4 4 4h16c2.21 0 4,-1.79 4,-4V14H12v24zM38 8h-7l-2,-2H19l-2 2h-7v4h28V8z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_person.xml b/packages/SystemUI/res/drawable/ic_person.xml
new file mode 100644
index 0000000..101911f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_person.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#ffffffff"
+        android:pathData="M24 24c4.42 0 8,-3.59 8,-8 0,-4.42,-3.58,-8,-8,-8s-8 3.58,-8 8c0 4.41 3.58 8 8 8zm0 4c-5.33 0,-16 2.67,-16 8v4h32v-4c0,-5.33,-10.67,-8,-16,-8z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 7586227..c86e9dc 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -41,8 +41,6 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingBottom="4dp"
-        android:paddingEnd="4dp"
-        android:paddingStart="4dp"
         android:paddingTop="6dp" >
 
         <!-- volume rows added and removed here! :-) -->
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index af27cc4..b51aa96 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -16,6 +16,8 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:paddingStart="4dp"
+    android:paddingEnd="4dp"
     android:clipChildren="false" >
 
     <TextView
diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml
index c491e9c..dcdc859 100644
--- a/packages/SystemUI/res/layout/volume_zen_footer.xml
+++ b/packages/SystemUI/res/layout/volume_zen_footer.xml
@@ -24,6 +24,8 @@
         android:id="@+id/volume_zen_switch_bar"
         android:layout_width="match_parent"
         android:layout_height="@dimen/volume_button_size"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp"
         android:clickable="true"
         android:orientation="horizontal" >
 
@@ -49,6 +51,7 @@
             android:layout_width="wrap_content"
             android:layout_height="fill_parent"
             android:layout_marginEnd="11dp" />
+
     </LinearLayout>
 
     <RelativeLayout
@@ -56,8 +59,8 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/volume_button_size"
         android:layout_marginStart="@dimen/volume_button_size"
-        android:paddingEnd="3dp"
-        android:paddingStart="3dp" >
+        android:paddingEnd="7dp"
+        android:paddingStart="7dp" >
 
         <TextView
             android:id="@+id/volume_zen_panel_summary_line_1"
@@ -79,6 +82,8 @@
         android:id="@+id/volume_zen_mode_panel_buttons"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp"
         android:gravity="end" >
 
         <TextView
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 3d73ff7..d829f0e 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -19,7 +19,6 @@
     android:id="@+id/zen_mode_panel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginStart="35dp"
     android:clipChildren="false"
     android:orientation="vertical" >
 
@@ -28,6 +27,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:minHeight="8dp"
+        android:layout_marginStart="39dp"
         android:elevation="4dp"
         android:background="@drawable/qs_background_secondary" >
 
@@ -46,15 +46,13 @@
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:visibility="gone"
-        android:layout_marginStart="@dimen/qs_panel_padding"
-        android:layout_marginEnd="@dimen/qs_panel_padding"
-        android:layout_marginBottom="@dimen/qs_panel_padding"
         android:background="#4dffffff" />
 
     <RelativeLayout
         android:id="@+id/zen_subhead"
         android:layout_width="match_parent"
         android:layout_height="62dp"
+        android:layout_marginStart="39dp"
         android:gravity="center_vertical"
         android:paddingLeft="8dp"
         android:paddingRight="8dp" >
@@ -98,10 +96,60 @@
 
     </RelativeLayout>
 
+    <RelativeLayout
+        android:id="@+id/zen_introduction"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingEnd="4dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:background="@color/zen_introduction_message_background" >
+
+        <ImageView
+            android:id="@+id/zen_introduction_confirm"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_alignParentEnd="true"
+            android:background="@drawable/btn_borderless_rect"
+            android:clickable="true"
+            android:contentDescription="@string/accessibility_desc_confirm"
+            android:scaleType="center"
+            android:src="@drawable/ic_close"
+            android:tint="@android:color/white" />
+
+        <TextView
+            android:id="@+id/zen_introduction_message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:layout_marginStart="55dp"
+            android:lineSpacingMultiplier="1.20029"
+            android:layout_toStartOf="@id/zen_introduction_confirm"
+            android:text="@string/zen_priority_introduction"
+            android:textAppearance="@style/TextAppearance.QS.VolumeSuppressor" />
+
+        <TextView
+            android:id="@+id/zen_introduction_customize"
+            style="@style/QSBorderlessButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_marginEnd="4dp"
+            android:layout_below="@id/zen_introduction_message"
+            android:clickable="true"
+            android:focusable="true"
+            android:text="@string/zen_priority_customize_button"
+            android:textAppearance="@style/TextAppearance.QS.DetailButton.White" />
+
+    </RelativeLayout>
+
     <LinearLayout
         android:id="@+id/zen_conditions"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginStart="39dp"
         android:orientation="vertical"
         android:paddingBottom="@dimen/zen_mode_condition_detail_bottom_padding" />
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ded7c4e..88d1769 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -44,6 +44,7 @@
     <color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
     <color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200 -->
     <color name="qs_detail_button">#FFB0BEC5</color><!-- 100% blue grey 200 -->
+    <color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
     <color name="qs_detail_transition">#66FFFFFF</color>
     <color name="qs_detail_progress_track">#99009688</color><!-- 60% deep teal 500 -->
     <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
@@ -137,4 +138,6 @@
     <color name="light_mode_icon_color_single_tone">#ffffff</color>
     <color name="light_mode_icon_color_dual_tone_background">#4dffffff</color>
     <color name="light_mode_icon_color_dual_tone_fill">#ffffff</color>
+
+    <color name="zen_introduction_message_background">#ff009688</color><!-- deep teal 500 -->
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fe974be..ae134c6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -410,6 +410,8 @@
     <string name="accessibility_desc_settings">Settings</string>
     <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_recent_apps">Overview.</string>
+    <!-- Content description for the confirm button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_confirm">Confirm</string>
 
     <!-- Content description of the user tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_user">User <xliff:g id="user" example="John Doe">%s</xliff:g>.</string>
@@ -723,6 +725,12 @@
     <!-- Zen mode: No interruptions title, with a warning about alarms. [CHAR LIMIT=60] -->
     <string name="zen_no_interruptions_with_warning">No interruptions. Not even alarms.</string>
 
+    <!-- Zen mode: Priority only introduction message on first use -->
+    <string name="zen_priority_introduction">You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events, and callers you specify.</string>
+
+    <!-- Zen mode: Priority only customization button label -->
+    <string name="zen_priority_customize_button">Customize</string>
+
     <!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
     <string name="zen_no_interruptions">No interruptions</string>
 
@@ -830,6 +838,15 @@
     <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
     <string name="guest_wipe_session_dontwipe">Yes, continue</string>
 
+    <!-- Title of the notification shown to a new guest user [CHAR LIMIT=60] -->
+    <string name="guest_notification_title">Guest user</string>
+
+    <!-- Text of the notification shown to a new guest user [CHAR LIMIT=60] -->
+    <string name="guest_notification_text">Remove guest to delete apps and data</string>
+
+    <!-- Remove action in the notification shown to a new guest user [CHAR LIMIT=30] -->
+    <string name="guest_notification_remove_action">REMOVE GUEST</string>
+
     <!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
     <string name="user_add_user_title" msgid="2108112641783146007">Add new user?</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 107a8ec..974cc48 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -151,6 +151,10 @@
         <item name="android:gravity">center</item>
     </style>
 
+    <style name="TextAppearance.QS.DetailButton.White">
+        <item name="android:textColor">@color/qs_detail_button_white</item>
+    </style>
+
     <style name="TextAppearance.QS.DetailEmpty">
         <item name="android:textSize">@dimen/qs_detail_empty_text_size</item>
         <item name="android:textColor">@color/qs_subhead</item>
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 9df67fd..68b1968 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -35,7 +35,10 @@
         Key.HOTSPOT_TILE_LAST_USED,
         Key.COLOR_INVERSION_TILE_LAST_USED,
         Key.DND_TILE_VISIBLE,
-        Key.DND_TILE_COMBINED_ICON
+        Key.DND_TILE_COMBINED_ICON,
+        Key.DND_CONFIRMED_PRIORITY_INTRODUCTION,
+        Key.DND_FAVORITE_BUCKET_INDEX,
+        Key.DND_NONE_SELECTED,
     })
     public @interface Key {
         String SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
@@ -44,6 +47,9 @@
         String COLOR_INVERSION_TILE_LAST_USED = "ColorInversionTileLastUsed";
         String DND_TILE_VISIBLE = "DndTileVisible";
         String DND_TILE_COMBINED_ICON = "DndTileCombinedIcon";
+        String DND_CONFIRMED_PRIORITY_INTRODUCTION = "DndConfirmedPriorityIntroduction";
+        String DND_FAVORITE_BUCKET_INDEX = "DndCountdownMinuteIndex";
+        String DND_NONE_SELECTED = "DndNoneSelected";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4ac41a1..194bcfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -19,6 +19,9 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -63,6 +66,7 @@
     private static final boolean DEBUG = false;
     private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
             "lockscreenSimpleUserSwitcher";
+    private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST";
 
     private final Context mContext;
     private final UserManager mUserManager;
@@ -89,6 +93,7 @@
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_STOPPING);
+        filter.addAction(ACTION_REMOVE_GUEST);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
                 null /* permission */, null /* scheduler */);
 
@@ -296,6 +301,22 @@
                 Log.v(TAG, "Broadcast: a=" + intent.getAction()
                        + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
             }
+            if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
+                int currentUser = ActivityManager.getCurrentUser();
+                UserInfo userInfo = mUserManager.getUserInfo(currentUser);
+                if (userInfo != null && userInfo.isGuest()) {
+                    showExitGuestDialog(currentUser);
+                }
+                return;
+            }
+            if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
+                final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                UserInfo userInfo = mUserManager.getUserInfo(currentId);
+                if (userInfo != null && userInfo.isGuest()) {
+                    showGuestNotification(currentId);
+                }
+            }
+
             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
                 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
                     mExitGuestDialog.cancel();
@@ -329,6 +350,24 @@
             }
             refreshUsers(forcePictureLoadForId);
         }
+
+        private void showGuestNotification(int guestUserId) {
+            PendingIntent removeGuestPI = PendingIntent.getBroadcastAsUser(mContext,
+                    0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.OWNER);
+            Notification notification = new Notification.Builder(mContext)
+                    .setVisibility(Notification.VISIBILITY_SECRET)
+                    .setPriority(Notification.PRIORITY_MIN)
+                    .setSmallIcon(R.drawable.ic_person)
+                    .setContentTitle(mContext.getString(R.string.guest_notification_title))
+                    .setContentText(mContext.getString(R.string.guest_notification_text))
+                    .setShowWhen(false)
+                    .addAction(R.drawable.ic_delete,
+                            mContext.getString(R.string.guest_notification_remove_action),
+                            removeGuestPI)
+                    .build();
+            NotificationManager.from(mContext).notifyAsUser(null, 0, notification,
+                    new UserHandle(guestUserId));
+        }
     };
 
     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 539fec8..bb4aa61 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -107,20 +107,22 @@
     private boolean mShowing;
     private boolean mExpanded;
     private int mActiveStream;
-    private boolean mShowHeaders = Prefs.DEFAULT_SHOW_HEADERS;
-    private boolean mShowFooter = Prefs.DEFAULT_SHOW_FOOTER;
-    private boolean mShowZenFooter = Prefs.DEFAULT_ZEN_FOOTER;
-    private boolean mAutomute = Prefs.DEFAULT_ENABLE_AUTOMUTE;
-    private boolean mSilentMode = Prefs.DEFAULT_ENABLE_SILENT_MODE;
+    private boolean mShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
+    private boolean mShowFooter = VolumePrefs.DEFAULT_SHOW_FOOTER;
+    private boolean mShowZenFooter = VolumePrefs.DEFAULT_ZEN_FOOTER;
+    private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
+    private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
     private int mExpandButtonRes;
     private boolean mExpanding;
     private SafetyWarningDialog mSafetyWarning;
+    private Callback mCallback;
 
     public VolumeDialog(Context context, VolumeDialogController controller,
-            ZenModeController zenModeController) {
+            ZenModeController zenModeController, Callback callback) {
         mContext = context;
         mController = controller;
+        mCallback = callback;
         mSpTexts = new SpTexts(mContext);
         mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
 
@@ -421,14 +423,6 @@
         mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
     }
 
-    protected void onSettingsClickedH() {
-        // hook for subclasses
-    }
-
-    protected void onZenSettingsClickedH() {
-        // hook for subclasses
-    }
-
     private void showH(int reason) {
         mHandler.removeMessages(H.SHOW);
         mHandler.removeMessages(H.DISMISS);
@@ -885,7 +879,9 @@
                 @Override
                 public void run() {
                     Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
-                    onSettingsClickedH();
+                    if (mCallback != null) {
+                        mCallback.onSettingsClicked();
+                    }
                 }
             }, WAIT_FOR_RIPPLE);
         }
@@ -912,13 +908,23 @@
         @Override
         public void onSettingsClicked() {
             dismiss(Events.DISMISS_REASON_SETTINGS_CLICKED);
-            onZenSettingsClickedH();
+            if (mCallback != null) {
+                mCallback.onZenSettingsClicked();
+            }
         }
 
         @Override
         public void onDoneClicked() {
             dismiss(Events.DISMISS_REASON_DONE_CLICKED);
         }
+
+        @Override
+        public void onPrioritySettingsClicked() {
+            dismiss(Events.DISMISS_REASON_SETTINGS_CLICKED);
+            if (mCallback != null) {
+                mCallback.onZenPrioritySettingsClicked();
+            }
+        }
     };
 
     private final class H extends Handler {
@@ -1052,10 +1058,16 @@
         private boolean important;
         private int cachedIconRes;
         private int iconState;  // from Events
-        private boolean cachedShowHeaders = Prefs.DEFAULT_SHOW_HEADERS;
+        private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
         private int cachedExpandButtonRes;
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
     }
+
+    public interface Callback {
+        void onSettingsClicked();
+        void onZenSettingsClicked();
+        void onZenPrioritySettingsClicked();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 741e498..86abfcc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -17,11 +17,13 @@
 package com.android.systemui.volume;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.VolumePolicy;
 import android.os.Bundle;
 import android.os.Handler;
+import android.provider.Settings;
 
 import com.android.systemui.SystemUI;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -41,6 +43,12 @@
     private final VolumeDialogController mController;
     private final ZenModeController mZenModeController;
     private final VolumeDialog mDialog;
+    private final VolumePolicy mVolumePolicy = new VolumePolicy(
+            true,  // volumeDownToEnterSilent
+            true,  // volumeUpToExitSilent
+            true,  // doNotDisturbWhenSilent
+            400    // vibrateToSilentDebounce
+    );
 
     public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler,
             ZenModeController zen) {
@@ -53,12 +61,7 @@
             }
         };
         mZenModeController = zen;
-        mDialog = new VolumeDialog(context, mController, zen) {
-            @Override
-            protected void onZenSettingsClickedH() {
-                startZenSettings();
-            }
-        };
+        mDialog = new VolumeDialog(context, mController, zen, mVolumeDialogCallback);
         applyConfiguration();
     }
 
@@ -77,7 +80,7 @@
         mDialog.setZenFooter(true);
         mDialog.setAutomute(true);
         mDialog.setSilentMode(false);
-        mController.setVolumePolicy(VolumePolicy.DEFAULT);
+        mController.setVolumePolicy(mVolumePolicy);
         mController.showDndTile(false);
     }
 
@@ -113,8 +116,26 @@
         mDialog.dump(pw);
     }
 
-    private void startZenSettings() {
-        mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
-                ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
+    private void startSettings(Intent intent) {
+        mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(intent,
+                true /* onlyProvisioned */, true /* dismissShade */);
     }
+
+    private final VolumeDialog.Callback mVolumeDialogCallback = new VolumeDialog.Callback() {
+        @Override
+        public void onSettingsClicked() {
+            startSettings(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
+        }
+
+        @Override
+        public void onZenSettingsClicked() {
+            startSettings(ZenModePanel.ZEN_SETTINGS);
+        }
+
+        @Override
+        public void onZenPrioritySettingsClicked() {
+            startSettings(ZenModePanel.ZEN_PRIORITY_SETTINGS);
+        }
+    };
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 5bc8c3e..012eb41 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -99,7 +99,7 @@
 
     private boolean mEnabled;
     private boolean mDestroyed;
-    private VolumePolicy mVolumePolicy = new VolumePolicy(true, true, false, 400);
+    private VolumePolicy mVolumePolicy;
     private boolean mShowDndTile = false;
 
     public VolumeDialogController(Context context, ComponentName component) {
@@ -147,6 +147,7 @@
 
     public void setVolumePolicy(VolumePolicy policy) {
         mVolumePolicy = policy;
+        if (mVolumePolicy == null) return;
         try {
             mAudio.setVolumePolicy(mVolumePolicy);
         } catch (NoSuchMethodError e) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index f16e9d2..45cb4a1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -428,6 +428,13 @@
             }
 
             @Override
+            public void onPrioritySettings() {
+                if (mCallback != null) {
+                    mCallback.onZenPrioritySettings();
+                }
+            }
+
+            @Override
             public void onInteraction() {
                 resetTimeout();
             }
@@ -1524,6 +1531,7 @@
 
     public interface Callback {
         void onZenSettings();
+        void onZenPrioritySettings();
         void onInteraction();
         void onVisible(boolean visible);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
index fa6ea9e..b072cab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
@@ -17,6 +17,7 @@
 package com.android.systemui.volume;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
@@ -74,6 +75,12 @@
             }
 
             @Override
+            public void onZenPrioritySettings() {
+                mHandler.removeCallbacks(mStartZenPrioritySettings);
+                mHandler.post(mStartZenPrioritySettings);
+            }
+
+            @Override
             public void onInteraction() {
                 final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
                 if (kvm != null) {
@@ -126,12 +133,23 @@
         mPanel.postDismiss(0);
     }
 
+    private void startSettings(Intent intent) {
+        mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(intent,
+                true /* onlyProvisioned */, true /* dismissShade */);
+        mPanel.postDismiss(mDismissDelay);
+    }
+
     private final Runnable mStartZenSettings = new Runnable() {
         @Override
         public void run() {
-            mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
-                    ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
-            mPanel.postDismiss(mDismissDelay);
+            startSettings(ZenModePanel.ZEN_SETTINGS);
+        }
+    };
+
+    private final Runnable mStartZenPrioritySettings = new Runnable() {
+        @Override
+        public void run() {
+            startSettings(ZenModePanel.ZEN_PRIORITY_SETTINGS);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Prefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/volume/Prefs.java
rename to packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
index 58bc9f4..915e998 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
@@ -24,7 +24,7 @@
 /**
  *  Configuration for the volume dialog + related policy.
  */
-public class Prefs {
+public class VolumePrefs {
 
     public static final String PREF_ENABLE_PROTOTYPE = "pref_enable_prototype";  // not persistent
     public static final String PREF_SHOW_ALARMS = "pref_show_alarms";
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 387aed0..e979786 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -36,6 +36,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.qs.tiles.DndTile;
@@ -172,18 +173,40 @@
         private static final String DISABLE = "com.android.systemui.vui.DISABLE";
         private static final String EXTRA_COMPONENT = "component";
 
+        private static final String PREF = "com.android.systemui.PREF";
+        private static final String EXTRA_KEY = "key";
+        private static final String EXTRA_VALUE = "value";
+
         public void start() {
             final IntentFilter filter = new IntentFilter();
             filter.addAction(ENABLE);
             filter.addAction(DISABLE);
+            filter.addAction(PREF);
             mContext.registerReceiver(this, filter, null, mHandler);
         }
 
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
+            if (PREF.equals(action)) {
+                final String key = intent.getStringExtra(EXTRA_KEY);
+                if (key != null && intent.getExtras() != null) {
+                    final Object value = intent.getExtras().get(EXTRA_VALUE);
+                    if (value == null) {
+                        Prefs.remove(mContext, key);
+                    } else if (value instanceof Boolean) {
+                        Prefs.putBoolean(mContext, key, (Boolean) value);
+                    } else if (value instanceof Integer) {
+                        Prefs.putInt(mContext, key, (Integer) value);
+                    } else if (value instanceof Long) {
+                        Prefs.putLong(mContext, key, (Long) value);
+                    }
+                }
+                return;
+            }
             final ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT);
-            final boolean current = component.equals(mVolumeControllerService.getComponent());
+            final boolean current = component != null
+                    && component.equals(mVolumeControllerService.getComponent());
             if (ENABLE.equals(action) && component != null) {
                 if (!current) {
                     showServiceActivationDialog(component);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index ef8257c..775c87d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -99,6 +99,7 @@
         mController = controller;
         mZenModePanel.init(controller);
         mZenModePanel.setEmbedded(true);
+        mZenModePanel.setCallback(mZenModePanelCallback);
         mSwitch.setOnCheckedChangeListener(mCheckedListener);
         mController.addCallback(new ZenModeController.Callback() {
             @Override
@@ -212,13 +213,38 @@
         Util.setText(mSummaryLine2, line2);
     }
 
+    private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
+        @Override
+        public void onMoreSettings() {
+            if (mCallback != null) {
+                mCallback.onSettingsClicked();
+            }
+        }
+
+        @Override
+        public void onPrioritySettings() {
+            if (mCallback != null) {
+                mCallback.onPrioritySettingsClicked();
+            }
+        }
+
+        @Override
+        public void onInteraction() {
+            // noop
+        }
+
+        @Override
+        public void onExpanded(boolean expanded) {
+            // noop
+        }
+    };
+
     private final OnCheckedChangeListener mCheckedListener = new OnCheckedChangeListener() {
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
             if (D.BUG) Log.d(TAG, "onCheckedChanged " + isChecked);
             if (isChecked != isZen()) {
-                final int newZen = isChecked ? Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                        : Global.ZEN_MODE_OFF;
+                final int newZen = isChecked ? Global.ZEN_MODE_ALARMS : Global.ZEN_MODE_OFF;
                 mZen = newZen;  // this one's optimistic
                 setFooterExpanded(isChecked);
                 mController.setZen(newZen, null, TAG);
@@ -230,5 +256,6 @@
         void onFooterExpanded();
         void onSettingsClicked();
         void onDoneClicked();
+        void onPrioritySettingsClicked();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index f6d4c36..1b563dc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -50,6 +50,7 @@
 import android.widget.RadioButton;
 import android.widget.TextView;
 
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
@@ -74,12 +75,15 @@
     private static final int FOREVER_CONDITION_INDEX = 0;
     private static final int COUNTDOWN_CONDITION_INDEX = 1;
 
-    public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+    public static final Intent ZEN_SETTINGS
+            = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+    public static final Intent ZEN_PRIORITY_SETTINGS
+            = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
 
     private final Context mContext;
     private final LayoutInflater mInflater;
     private final H mHandler = new H();
-    private final Prefs mPrefs;
+    private final ZenPrefs mPrefs;
     private final IconPulser mIconPulser;
     private final int mSubheadWarningColor;
     private final int mSubheadColor;
@@ -96,6 +100,9 @@
     private TextView mZenSubheadExpanded;
     private View mZenEmbeddedDivider;
     private View mMoreSettings;
+    private View mZenIntroduction;
+    private View mZenIntroductionConfirm;
+    private View mZenIntroductionCustomize;
     private LinearLayout mZenConditions;
 
     private Callback mCallback;
@@ -121,7 +128,7 @@
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
-        mPrefs = new Prefs();
+        mPrefs = new ZenPrefs();
         mInflater = LayoutInflater.from(mContext.getApplicationContext());
         mIconPulser = new IconPulser(mContext);
         mSubheadWarningColor = context.getColor(R.color.system_warning_color);
@@ -202,11 +209,36 @@
         });
         Interaction.register(mMoreSettings, mInteractionCallback);
 
+        mZenIntroduction = findViewById(R.id.zen_introduction);
+        mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
+        mZenIntroductionConfirm.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                confirmZenIntroduction();
+            }
+        });
+        mZenIntroductionCustomize = findViewById(R.id.zen_introduction_customize);
+        mZenIntroductionCustomize.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                confirmZenIntroduction();
+                if (mCallback != null) {
+                    mCallback.onPrioritySettings();
+                }
+            }
+        });
+
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
 
         setLayoutTransition(newLayoutTransition(mTransitionHelper));
     }
 
+    private void confirmZenIntroduction() {
+        if (DEBUG) Log.d(TAG, "confirmZenIntroduction");
+        Prefs.putBoolean(mContext, Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, true);
+        mHandler.sendEmptyMessage(H.UPDATE_WIDGETS);
+    }
+
     private LayoutTransition newLayoutTransition(TransitionListener listener) {
         final LayoutTransition transition = new LayoutTransition();
         transition.disableTransitionType(LayoutTransition.DISAPPEARING);
@@ -452,13 +484,15 @@
         final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
         final boolean expanded = !mHidden && mExpanded;
+        final boolean conditions = mEmbedded || !zenOff && expanded;
+        final boolean introduction = conditions && zenImportant && !mPrefs.mConfirmedIntroduction;
 
         mZenButtons.setVisibility(mHidden ? GONE : VISIBLE);
         mZenSubhead.setVisibility(!mHidden && !zenOff && !mEmbedded ? VISIBLE : GONE);
         mZenSubheadExpanded.setVisibility(expanded ? VISIBLE : GONE);
         mZenSubheadCollapsed.setVisibility(!expanded ? VISIBLE : GONE);
         mMoreSettings.setVisibility(zenImportant && expanded ? VISIBLE : GONE);
-        mZenConditions.setVisibility(mEmbedded || !zenOff && expanded ? VISIBLE : GONE);
+        mZenConditions.setVisibility(conditions ? VISIBLE : GONE);
 
         if (zenNone) {
             mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);
@@ -469,6 +503,7 @@
         }
         mZenSubheadExpanded.setTextColor(zenNone && mPrefs.isNoneDangerous()
                 ? mSubheadWarningColor : mSubheadColor);
+        mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE);
     }
 
     private static Condition parseExistingTimeCondition(Context context, Condition condition) {
@@ -835,6 +870,7 @@
     private final class H extends Handler {
         private static final int UPDATE_CONDITIONS = 1;
         private static final int MANUAL_RULE_CHANGED = 2;
+        private static final int UPDATE_WIDGETS = 3;
 
         private H() {
             super(Looper.getMainLooper());
@@ -842,16 +878,17 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (msg.what == UPDATE_CONDITIONS) {
-                handleUpdateConditions((Condition[]) msg.obj);
-            } else if (msg.what == MANUAL_RULE_CHANGED) {
-                handleUpdateManualRule((ZenRule) msg.obj);
+            switch (msg.what) {
+                case UPDATE_CONDITIONS: handleUpdateConditions((Condition[]) msg.obj); break;
+                case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
+                case UPDATE_WIDGETS: updateWidgets(); break;
             }
         }
     }
 
     public interface Callback {
         void onMoreSettings();
+        void onPrioritySettings();
         void onInteraction();
         void onExpanded(boolean expanded);
     }
@@ -865,21 +902,20 @@
         Condition condition;
     }
 
-    private final class Prefs implements OnSharedPreferenceChangeListener {
-        private static final String KEY_MINUTE_INDEX = "minuteIndex";
-        private static final String KEY_NONE_SELECTED = "noneSelected";
-
+    private final class ZenPrefs implements OnSharedPreferenceChangeListener {
         private final int mNoneDangerousThreshold;
 
         private int mMinuteIndex;
         private int mNoneSelected;
+        private boolean mConfirmedIntroduction;
 
-        private Prefs() {
+        private ZenPrefs() {
             mNoneDangerousThreshold = mContext.getResources()
                     .getInteger(R.integer.zen_mode_alarm_warning_threshold);
-            prefs().registerOnSharedPreferenceChangeListener(this);
+            Prefs.registerListener(mContext, this);
             updateMinuteIndex();
             updateNoneSelected();
+            updateConfirmedIntroduction();
         }
 
         public boolean isNoneDangerous() {
@@ -890,7 +926,7 @@
             mNoneSelected = clampNoneSelected(mNoneSelected + 1);
             if (DEBUG) Log.d(mTag, "Setting none selected: " + mNoneSelected + " threshold="
                     + mNoneDangerousThreshold);
-            prefs().edit().putInt(KEY_NONE_SELECTED, mNoneSelected).apply();
+            Prefs.putInt(mContext, Prefs.Key.DND_NONE_SELECTED, mNoneSelected);
         }
 
         public int getMinuteIndex() {
@@ -902,21 +938,19 @@
             if (minuteIndex == mMinuteIndex) return;
             mMinuteIndex = clampIndex(minuteIndex);
             if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex);
-            prefs().edit().putInt(KEY_MINUTE_INDEX, mMinuteIndex).apply();
+            Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_BUCKET_INDEX, mMinuteIndex);
         }
 
         @Override
         public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
             updateMinuteIndex();
             updateNoneSelected();
-        }
-
-        private SharedPreferences prefs() {
-            return mContext.getSharedPreferences(mContext.getPackageName(), 0);
+            updateConfirmedIntroduction();
         }
 
         private void updateMinuteIndex() {
-            mMinuteIndex = clampIndex(prefs().getInt(KEY_MINUTE_INDEX, DEFAULT_BUCKET_INDEX));
+            mMinuteIndex = clampIndex(Prefs.getInt(mContext,
+                    Prefs.Key.DND_FAVORITE_BUCKET_INDEX, DEFAULT_BUCKET_INDEX));
             if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex);
         }
 
@@ -925,13 +959,22 @@
         }
 
         private void updateNoneSelected() {
-            mNoneSelected = clampNoneSelected(prefs().getInt(KEY_NONE_SELECTED, 0));
+            mNoneSelected = clampNoneSelected(Prefs.getInt(mContext,
+                    Prefs.Key.DND_NONE_SELECTED, 0));
             if (DEBUG) Log.d(mTag, "None selected: " + mNoneSelected);
         }
 
         private int clampNoneSelected(int noneSelected) {
             return MathUtils.constrain(noneSelected, 0, Integer.MAX_VALUE);
         }
+
+        private void updateConfirmedIntroduction() {
+            final boolean confirmed =  Prefs.getBoolean(mContext,
+                    Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, false);
+            if (confirmed == mConfirmedIntroduction) return;
+            mConfirmedIntroduction = confirmed;
+            if (DEBUG) Log.d(mTag, "Confirmed introduction: " + mConfirmedIntroduction);
+        }
     }
 
     private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 5fc1f93..7c927fd 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -332,10 +332,12 @@
       rsnClosureSetGlobal(mContext, closureID, fieldID, value, size);
     }
 
-    native long rsnScriptGroup2Create(long con, String cachePath, long[] closures);
-    synchronized long nScriptGroup2Create(String cachePath, long[] closures) {
+    native long rsnScriptGroup2Create(long con, String name, String cachePath,
+                                      long[] closures);
+    synchronized long nScriptGroup2Create(String name, String cachePath,
+                                          long[] closures) {
       validate();
-      return rsnScriptGroup2Create(mContext, cachePath, closures);
+      return rsnScriptGroup2Create(mContext, name, cachePath, closures);
     }
 
     native void rsnScriptGroup2Execute(long con, long groupID);
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index 858a957..96bb6e2 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -323,6 +323,7 @@
         }
     }
 
+    String mName;
     List<Closure> mClosures;
     List<UnboundValue> mInputs;
     Future[] mOutputs;
@@ -333,9 +334,10 @@
         super(id, rs);
     }
 
-    ScriptGroup2(RenderScript rs, List<Closure> closures,
+    ScriptGroup2(RenderScript rs, String name, List<Closure> closures,
                  List<UnboundValue> inputs, Future[] outputs) {
         super(0, rs);
+        mName = name;
         mClosures = closures;
         mInputs = inputs;
         mOutputs = outputs;
@@ -344,7 +346,7 @@
         for (int i = 0; i < closureIDs.length; i++) {
             closureIDs[i] = closures.get(i).getID(rs);
         }
-        long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs);
+        long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs);
         setID(id);
     }
 
@@ -560,7 +562,7 @@
                 !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
                 throw new RSIllegalArgumentException("invalid script group name");
             }
-            ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
+            ScriptGroup2 ret = new ScriptGroup2(mRS, name, mClosures, mInputs, outputs);
             return ret;
         }
 
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 783df68..49afa6d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -418,8 +418,9 @@
 }
 
 static long
-nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
+nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
                     jstring cacheDir, jlongArray closureArray) {
+  AutoJavaStringToUTF8 nameUTF(_env, name);
   AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
 
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
@@ -430,7 +431,8 @@
   }
 
   return (jlong)(uintptr_t)rsScriptGroup2Create(
-      (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(),
+      (RsContext)con, nameUTF.c_str(), nameUTF.length(),
+      cacheDirUTF.c_str(), cacheDirUTF.length(),
       closures, numClosures);
 }
 
@@ -2404,7 +2406,7 @@
 {"rsnScriptInvokeIDCreate",          "(JJI)J",                                (void*)nScriptInvokeIDCreate },
 {"rsnScriptFieldIDCreate",           "(JJI)J",                                (void*)nScriptFieldIDCreate },
 {"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
-{"rsnScriptGroup2Create",            "(JLjava/lang/String;[J)J",               (void*)nScriptGroup2Create },
+{"rsnScriptGroup2Create",            "(JLjava/lang/String;Ljava/lang/String;[J)J", (void*)nScriptGroup2Create },
 {"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
 {"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
 {"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 34e8b78..9f80fd8 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -99,6 +99,7 @@
     static final boolean DEBUG_BATCH = localLOGV || false;
     static final boolean DEBUG_VALIDATE = localLOGV || false;
     static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
+    static final boolean RECORD_ALARMS_IN_HISTORY = true;
     static final int ALARM_EVENT = 1;
     static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
@@ -1345,15 +1346,6 @@
     }
 
     void rescheduleKernelAlarmsLocked() {
-        if (mPendingIdleUntil != null) {
-            // If we have a pending "idle until" alarm, we will just blindly wait until
-            // it is time for that alarm to go off.  We don't want to wake up for any
-            // other reasons.
-            mNextWakeup = mNextNonWakeup = mPendingIdleUntil.whenElapsed;
-            setLocked(ELAPSED_REALTIME_WAKEUP, mNextWakeup);
-            setLocked(ELAPSED_REALTIME, mNextNonWakeup);
-            return;
-        }
         // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
         // prior to that which contains no wakeups, we schedule that as well.
         long nextNonWakeup = 0;
@@ -1574,13 +1566,6 @@
     boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
             final long nowRTC) {
         boolean hasWakeup = false;
-        if (mPendingIdleUntil != null) {
-            // If we have a pending "idle until" alarm, don't trigger any alarms
-            // until we are past the idle period.
-            if (nowELAPSED < mPendingIdleUntil.whenElapsed) {
-                return false;
-            }
-        }
         // batches are temporally sorted, so we need only pull from the
         // start of the list until we either empty it or hit a batch
         // that is not yet deliverable
@@ -1816,6 +1801,17 @@
                 if (localLOGV) {
                     Slog.v(TAG, "sending alarm " + alarm);
                 }
+                if (RECORD_ALARMS_IN_HISTORY) {
+                    if (alarm.workSource != null && alarm.workSource.size() > 0) {
+                        for (int wi=0; wi<alarm.workSource.size(); wi++) {
+                            ActivityManagerNative.noteAlarmStart(
+                                    alarm.operation, alarm.workSource.get(wi), alarm.tag);
+                        }
+                    } else {
+                        ActivityManagerNative.noteAlarmStart(
+                                alarm.operation, -1, alarm.tag);
+                    }
+                }
                 alarm.operation.send(getContext(), 0,
                         mBackgroundIntent.putExtra(
                                 Intent.EXTRA_ALARM_COUNT, alarm.count),
@@ -1856,11 +1852,11 @@
                         for (int wi=0; wi<alarm.workSource.size(); wi++) {
                             ActivityManagerNative.noteWakeupAlarm(
                                     alarm.operation, alarm.workSource.get(wi),
-                                    alarm.workSource.getName(wi));
+                                    alarm.workSource.getName(wi), alarm.tag);
                         }
                     } else {
                         ActivityManagerNative.noteWakeupAlarm(
-                                alarm.operation, -1, null);
+                                alarm.operation, -1, null, alarm.tag);
                     }
                 }
             } catch (PendingIntent.CanceledException e) {
@@ -2222,6 +2218,17 @@
                         fs.nesting = 0;
                         fs.aggregateTime += nowELAPSED - fs.startTime;
                     }
+                    if (RECORD_ALARMS_IN_HISTORY) {
+                        if (inflight.mWorkSource != null && inflight.mWorkSource.size() > 0) {
+                            for (int wi=0; wi<inflight.mWorkSource.size(); wi++) {
+                                ActivityManagerNative.noteAlarmFinish(
+                                        pi, inflight.mWorkSource.get(wi), inflight.mTag);
+                            }
+                        } else {
+                            ActivityManagerNative.noteAlarmFinish(
+                                    pi, -1, inflight.mTag);
+                        }
+                    }
                 } else {
                     mLog.w("No in-flight alarm for " + pi + " " + intent);
                 }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 2e6d1c1..ee73b1a 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -44,12 +44,14 @@
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
 import android.security.KeyStore;
+import android.service.gatekeeper.IGateKeeperService;
 import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LockSettingsStorage.CredentialHash;
 
 import java.util.Arrays;
 import java.util.List;
@@ -72,6 +74,7 @@
 
     private LockPatternUtils mLockPatternUtils;
     private boolean mFirstCallToVold;
+    private IGateKeeperService mGateKeeperService;
 
     public LockSettingsService(Context context) {
         mContext = context;
@@ -131,6 +134,7 @@
 
     public void systemReady() {
         migrateOldData();
+        getGateKeeperService();
         mStorage.prefetchUser(UserHandle.USER_OWNER);
     }
 
@@ -277,7 +281,6 @@
     @Override
     public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
         checkReadPermission(key, userId);
-
         String value = getStringUnchecked(key, null, userId);
         return TextUtils.isEmpty(value) ?
                 defaultValue : (value.equals("1") || value.equals("true"));
@@ -345,62 +348,252 @@
         }
     }
 
-    @Override
-    public void setLockPattern(String pattern, int userId) throws RemoteException {
-        checkWritePermission(userId);
 
-        maybeUpdateKeystore(pattern, userId);
+    private byte[] getCurrentHandle(int userId) {
+        CredentialHash credential;
+        byte[] currentHandle;
 
-        final byte[] hash = LockPatternUtils.patternToHash(
-                LockPatternUtils.stringToPattern(pattern));
-        mStorage.writePatternHash(hash, userId);
+        int currentHandleType = mStorage.getStoredCredentialType(userId);
+        switch (currentHandleType) {
+            case CredentialHash.TYPE_PATTERN:
+                credential = mStorage.readPatternHash(userId);
+                currentHandle = credential != null
+                        ? credential.hash
+                        : null;
+                break;
+            case CredentialHash.TYPE_PASSWORD:
+                credential = mStorage.readPasswordHash(userId);
+                currentHandle = credential != null
+                        ? credential.hash
+                        : null;
+                break;
+            case CredentialHash.TYPE_NONE:
+            default:
+                currentHandle = null;
+                break;
+        }
+
+        // sanity check
+        if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) {
+            Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available");
+        }
+
+        return currentHandle;
     }
 
+
     @Override
-    public void setLockPassword(String password, int userId) throws RemoteException {
+    public void setLockPattern(String pattern, String savedCredential, int userId)
+            throws RemoteException {
+        byte[] currentHandle = getCurrentHandle(userId);
+
+        if (pattern == null) {
+            mStorage.writePatternHash(null, userId);
+            return;
+        }
+
+        if (currentHandle == null) {
+            if (savedCredential != null) {
+                Slog.w(TAG, "Saved credential provided, but none stored");
+            }
+            savedCredential = null;
+        }
+
+        byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
+        if (enrolledHandle != null) {
+            mStorage.writePatternHash(enrolledHandle, userId);
+        } else {
+            Slog.e(TAG, "Failed to enroll pattern");
+        }
+    }
+
+
+    @Override
+    public void setLockPassword(String password, String savedCredential, int userId)
+            throws RemoteException {
+        byte[] currentHandle = getCurrentHandle(userId);
+
+        if (password == null) {
+            mStorage.writePasswordHash(null, userId);
+            return;
+        }
+
+        if (currentHandle == null) {
+            if (savedCredential != null) {
+                Slog.w(TAG, "Saved credential provided, but none stored");
+            }
+            savedCredential = null;
+        }
+
+        byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
+        if (enrolledHandle != null) {
+            mStorage.writePasswordHash(enrolledHandle, userId);
+        } else {
+            Slog.e(TAG, "Failed to enroll password");
+        }
+    }
+
+    private byte[] enrollCredential(byte[] enrolledHandle,
+            String enrolledCredential, String toEnroll, int userId)
+            throws RemoteException {
         checkWritePermission(userId);
+        byte[] enrolledCredentialBytes = enrolledCredential == null
+                ? null
+                : enrolledCredential.getBytes();
+        byte[] toEnrollBytes = toEnroll == null
+                ? null
+                : toEnroll.getBytes();
+        byte[] hash = getGateKeeperService().enroll(userId, enrolledHandle, enrolledCredentialBytes,
+                toEnrollBytes);
 
-        maybeUpdateKeystore(password, userId);
+        if (hash != null) {
+            maybeUpdateKeystore(toEnroll, userId);
+        }
 
-        mStorage.writePasswordHash(mLockPatternUtils.passwordToHash(password, userId), userId);
+        return hash;
     }
 
     @Override
     public boolean checkPattern(String pattern, int userId) throws RemoteException {
-        checkPasswordReadPermission(userId);
-        byte[] hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(pattern));
-        byte[] storedHash = mStorage.readPatternHash(userId);
-
-        if (storedHash == null) {
-            return true;
+        try {
+            doVerifyPattern(pattern, false, 0, userId);
+        } catch (VerificationFailedException ex) {
+            return false;
         }
 
-        boolean matched = Arrays.equals(hash, storedHash);
-        if (matched && !TextUtils.isEmpty(pattern)) {
-            maybeUpdateKeystore(pattern, userId);
+        return true;
+    }
+
+    @Override
+    public byte[] verifyPattern(String pattern, long challenge, int userId)
+            throws RemoteException {
+        try {
+            return doVerifyPattern(pattern, true, challenge, userId);
+        } catch (VerificationFailedException ex) {
+            return null;
         }
-        return matched;
+    }
+
+    private byte[] doVerifyPattern(String pattern, boolean hasChallenge, long challenge,
+            int userId) throws VerificationFailedException, RemoteException {
+       checkPasswordReadPermission(userId);
+
+       CredentialHash storedHash = mStorage.readPatternHash(userId);
+
+        if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(pattern)) {
+            // don't need to pass empty passwords to GateKeeper
+            return null;
+        }
+
+        if (TextUtils.isEmpty(pattern)) {
+            throw new VerificationFailedException();
+        }
+
+        if (storedHash.version == CredentialHash.VERSION_LEGACY) {
+            byte[] hash = mLockPatternUtils.patternToHash(
+                    mLockPatternUtils.stringToPattern(pattern));
+            if (Arrays.equals(hash, storedHash.hash)) {
+                maybeUpdateKeystore(pattern, userId);
+                // migrate password to GateKeeper
+                setLockPattern(pattern, null, userId);
+                if (!hasChallenge) {
+                    return null;
+                }
+                // Fall through to get the auth token. Technically this should never happen,
+                // as a user that had a legacy pattern would have to unlock their device
+                // before getting to a flow with a challenge, but supporting for consistency.
+            } else {
+                throw new VerificationFailedException();
+            }
+        }
+
+        byte[] token = null;
+        if (hasChallenge) {
+            token = getGateKeeperService()
+                    .verifyChallenge(userId, challenge, storedHash.hash, pattern.getBytes());
+            if (token == null) {
+                throw new VerificationFailedException();
+            }
+        } else if (!getGateKeeperService().verify(userId, storedHash.hash, pattern.getBytes())) {
+            throw new VerificationFailedException();
+        }
+
+        // pattern has matched
+        maybeUpdateKeystore(pattern, userId);
+        return token;
+
     }
 
     @Override
     public boolean checkPassword(String password, int userId) throws RemoteException {
-        checkPasswordReadPermission(userId);
-
-        byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
-        byte[] storedHash = mStorage.readPasswordHash(userId);
-
-        if (storedHash == null) {
-            return true;
+        try {
+            doVerifyPassword(password, false, 0, userId);
+        } catch (VerificationFailedException ex) {
+            return false;
         }
-
-        boolean matched = Arrays.equals(hash, storedHash);
-        if (matched && !TextUtils.isEmpty(password)) {
-            maybeUpdateKeystore(password, userId);
-        }
-        return matched;
+        return true;
     }
 
     @Override
+    public byte[] verifyPassword(String password, long challenge, int userId)
+            throws RemoteException {
+        try {
+            return doVerifyPassword(password, true, challenge, userId);
+        } catch (VerificationFailedException ex) {
+            return null;
+        }
+    }
+
+    private byte[] doVerifyPassword(String password, boolean hasChallenge, long challenge,
+            int userId) throws VerificationFailedException, RemoteException {
+       checkPasswordReadPermission(userId);
+
+       CredentialHash storedHash = mStorage.readPasswordHash(userId);
+
+        if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(password)) {
+            // don't need to pass empty passwords to GateKeeper
+            return null;
+        }
+
+        if (TextUtils.isEmpty(password)) {
+            throw new VerificationFailedException();
+        }
+
+        if (storedHash.version == CredentialHash.VERSION_LEGACY) {
+            byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
+            if (Arrays.equals(hash, storedHash.hash)) {
+                maybeUpdateKeystore(password, userId);
+                // migrate password to GateKeeper
+                setLockPassword(password, null, userId);
+                if (!hasChallenge) {
+                    return null;
+                }
+                // Fall through to get the auth token. Technically this should never happen,
+                // as a user that had a legacy password would have to unlock their device
+                // before getting to a flow with a challenge, but supporting for consistency.
+            } else {
+                throw new VerificationFailedException();
+            }
+        }
+
+        byte[] token = null;
+        if (hasChallenge) {
+            token = getGateKeeperService()
+                    .verifyChallenge(userId, challenge, storedHash.hash, password.getBytes());
+            if (token == null) {
+                throw new VerificationFailedException();
+            }
+        } else if (!getGateKeeperService().verify(userId, storedHash.hash, password.getBytes())) {
+            throw new VerificationFailedException();
+        }
+
+        // password has matched
+        maybeUpdateKeystore(password, userId);
+        return token;
+    }
+
+
+    @Override
     public boolean checkVoldPassword(int userId) throws RemoteException {
         if (!mFirstCallToVold) {
             return false;
@@ -497,4 +690,23 @@
         }
         return null;
     }
+
+    private synchronized IGateKeeperService getGateKeeperService() {
+        if (mGateKeeperService != null) {
+            return mGateKeeperService;
+        }
+
+        final IBinder service =
+            ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
+        if (service != null) {
+            mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
+            return mGateKeeperService;
+        }
+
+        Slog.e(TAG, "Unable to acquire GateKeeperService");
+        return null;
+    }
+
+    private class VerificationFailedException extends Exception {}
+
 }
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index d81daa9..f202c36 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -56,8 +56,10 @@
     };
 
     private static final String SYSTEM_DIRECTORY = "/system/";
-    private static final String LOCK_PATTERN_FILE = "gesture.key";
-    private static final String LOCK_PASSWORD_FILE = "password.key";
+    private static final String LOCK_PATTERN_FILE = "gatekeeper.gesture.key";
+    private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key";
+    private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
+    private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key";
 
     private static final Object DEFAULT = new Object();
 
@@ -66,6 +68,25 @@
     private final Cache mCache = new Cache();
     private final Object mFileWriteLock = new Object();
 
+    private int mStoredCredentialType;
+
+    class CredentialHash {
+        static final int TYPE_NONE = -1;
+        static final int TYPE_PATTERN = 1;
+        static final int TYPE_PASSWORD = 2;
+
+        static final int VERSION_LEGACY = 0;
+        static final int VERSION_GATEKEEPER = 1;
+
+        CredentialHash(byte[] hash, int version) {
+            this.hash = hash;
+            this.version = version;
+        }
+
+        byte[] hash;
+        int version;
+    }
+
     public LockSettingsStorage(Context context, Callback callback) {
         mContext = context;
         mOpenHelper = new DatabaseHelper(context, callback);
@@ -148,28 +169,72 @@
         readPatternHash(userId);
     }
 
-    public byte[] readPasswordHash(int userId) {
-        final byte[] stored = readFile(getLockPasswordFilename(userId));
-        if (stored != null && stored.length > 0) {
-            return stored;
+    public int getStoredCredentialType(int userId) {
+        if (mStoredCredentialType != 0) {
+            return mStoredCredentialType;
         }
+
+        CredentialHash pattern = readPatternHash(userId);
+        if (pattern == null) {
+            if (readPasswordHash(userId) != null) {
+                mStoredCredentialType = CredentialHash.TYPE_PASSWORD;
+            } else {
+                mStoredCredentialType = CredentialHash.TYPE_NONE;
+            }
+        } else {
+            CredentialHash password = readPasswordHash(userId);
+            if (password != null) {
+                // Both will never be GateKeeper
+                if (password.version == CredentialHash.VERSION_GATEKEEPER) {
+                    mStoredCredentialType = CredentialHash.TYPE_PASSWORD;
+                } else {
+                    mStoredCredentialType = CredentialHash.TYPE_PATTERN;
+                }
+            } else {
+                mStoredCredentialType = CredentialHash.TYPE_PATTERN;
+            }
+        }
+
+        return mStoredCredentialType;
+    }
+
+
+    public CredentialHash readPasswordHash(int userId) {
+        byte[] stored = readFile(getLockPasswordFilename(userId));
+        if (stored != null && stored.length > 0) {
+            return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
+        }
+
+        stored = readFile(getLegacyLockPasswordFilename(userId));
+        if (stored != null && stored.length > 0) {
+            return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+        }
+
         return null;
     }
 
-    public byte[] readPatternHash(int userId) {
-        final byte[] stored = readFile(getLockPatternFilename(userId));
+    public CredentialHash readPatternHash(int userId) {
+        byte[] stored = readFile(getLockPatternFilename(userId));
         if (stored != null && stored.length > 0) {
-            return stored;
+            return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER);
         }
+
+        stored = readFile(getLegacyLockPatternFilename(userId));
+        if (stored != null && stored.length > 0) {
+            return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+        }
+
         return null;
     }
 
     public boolean hasPassword(int userId) {
-        return hasFile(getLockPasswordFilename(userId));
+        return hasFile(getLockPasswordFilename(userId)) ||
+            hasFile(getLegacyLockPasswordFilename(userId));
     }
 
     public boolean hasPattern(int userId) {
-        return hasFile(getLockPatternFilename(userId));
+        return hasFile(getLockPatternFilename(userId)) ||
+            hasFile(getLegacyLockPatternFilename(userId));
     }
 
     private boolean hasFile(String name) {
@@ -237,6 +302,9 @@
     }
 
     public void writePatternHash(byte[] hash, int userId) {
+        mStoredCredentialType = hash == null
+            ? CredentialHash.TYPE_NONE
+            : CredentialHash.TYPE_PATTERN;
         writeFile(getLockPatternFilename(userId), hash);
         clearPasswordHash(userId);
     }
@@ -246,6 +314,9 @@
     }
 
     public void writePasswordHash(byte[] hash, int userId) {
+        mStoredCredentialType = hash == null
+            ? CredentialHash.TYPE_NONE
+            : CredentialHash.TYPE_PASSWORD;
         writeFile(getLockPasswordFilename(userId), hash);
         clearPatternHash(userId);
     }
@@ -264,6 +335,16 @@
         return getLockCredentialFilePathForUser(userId, LOCK_PASSWORD_FILE);
     }
 
+    @VisibleForTesting
+    String getLegacyLockPatternFilename(int userId) {
+        return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PATTERN_FILE);
+    }
+
+    @VisibleForTesting
+    String getLegacyLockPasswordFilename(int userId) {
+        return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PASSWORD_FILE);
+    }
+
     private String getLockCredentialFilePathForUser(int userId, String basename) {
         userId = getUserParentOrSelfId(userId);
         String dataSystemDirectory =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6341807..b606353 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -42,7 +42,6 @@
 import android.app.ITaskStackListener;
 import android.app.ProfilerInfo;
 import android.app.usage.UsageEvents;
-import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.res.Resources;
@@ -3449,6 +3448,35 @@
         }
     }
 
+    @Override
+    public int getPackageProcessState(String packageName) {
+        int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+        synchronized (this) {
+            for (int i=mLruProcesses.size()-1; i>=0; i--) {
+                final ProcessRecord proc = mLruProcesses.get(i);
+                if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT
+                        || procState > proc.setProcState) {
+                    boolean found = false;
+                    for (int j=proc.pkgList.size()-1; j>=0 && !found; j--) {
+                        if (proc.pkgList.keyAt(j).equals(packageName)) {
+                            procState = proc.setProcState;
+                            found = true;
+                        }
+                    }
+                    if (proc.pkgDeps != null && !found) {
+                        for (int j=proc.pkgDeps.size()-1; j>=0; j--) {
+                            if (proc.pkgDeps.valueAt(j).equals(packageName)) {
+                                procState = proc.setProcState;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return procState;
+    }
+
     private void dispatchProcessesChanged() {
         int N;
         synchronized (this) {
@@ -10501,15 +10529,11 @@
                 Context.WINDOW_SERVICE)).addView(v, lp);
     }
 
-    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg) {
+    public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag) {
         if (!(sender instanceof PendingIntentRecord)) {
             return;
         }
         final PendingIntentRecord rec = (PendingIntentRecord)sender;
-        final String tag;
-        synchronized (this) {
-            tag = getTagForIntentSenderLocked(rec, "*walarm*:");
-        }
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         synchronized (stats) {
             if (mBatteryStatsService.isOnBattery()) {
@@ -10524,6 +10548,34 @@
         }
     }
 
+    public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag) {
+        if (!(sender instanceof PendingIntentRecord)) {
+            return;
+        }
+        final PendingIntentRecord rec = (PendingIntentRecord)sender;
+        final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        synchronized (stats) {
+            mBatteryStatsService.enforceCallingPermission();
+            int MY_UID = Binder.getCallingUid();
+            int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+            mBatteryStatsService.noteAlarmStart(tag, sourceUid >= 0 ? sourceUid : uid);
+        }
+    }
+
+    public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag) {
+        if (!(sender instanceof PendingIntentRecord)) {
+            return;
+        }
+        final PendingIntentRecord rec = (PendingIntentRecord)sender;
+        final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        synchronized (stats) {
+            mBatteryStatsService.enforceCallingPermission();
+            int MY_UID = Binder.getCallingUid();
+            int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+            mBatteryStatsService.noteAlarmFinish(tag, sourceUid >= 0 ? sourceUid : uid);
+        }
+    }
+
     public boolean killPids(int[] pids, String pReason, boolean secure) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("killPids only available to the system");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ed108c2..bfc4fc7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -315,6 +315,20 @@
         }
     }
 
+    public void noteAlarmStart(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAlarmStartLocked(name, uid);
+        }
+    }
+
+    public void noteAlarmFinish(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAlarmFinishLocked(name, uid);
+        }
+    }
+
     public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging) {
         enforceCallingPermission();
@@ -884,7 +898,7 @@
         pw.println("    Enable or disable a running option.  Option state is not saved across boots.");
         pw.println("    Options are:");
         pw.println("      full-history: include additional detailed events in battery history:");
-        pw.println("          wake_lock_in and proc events");
+        pw.println("          wake_lock_in, alarms and proc events");
         pw.println("      no-auto-reset: don't automatically reset stats when unplugged");
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1eddc8e..9a30f0d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5577,8 +5577,9 @@
     @Override
     public void setVolumePolicy(VolumePolicy policy) {
         enforceVolumeController("set volume policy");
-        if (policy != null) {
+        if (policy != null && !policy.equals(mVolumePolicy)) {
             mVolumePolicy = policy;
+            if (DEBUG_VOL) Log.d(TAG, "Volume policy changed: " + mVolumePolicy);
         }
     }
 
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 8a9f3e1..23d5c05 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -49,16 +49,12 @@
     private static volatile AppIdleController sController;
     final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
     private final UsageStatsManagerInternal mUsageStatsInternal;
-    private final BatteryManagerInternal mBatteryManagerInternal;
+    private final BatteryManager mBatteryManager;
     private boolean mPluggedIn;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
-                int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
-                // TODO: Allow any charger type
-                onPluggedIn((plugged & BatteryManager.BATTERY_PLUGGED_AC) != 0);
-            }
+            onPluggedIn(mBatteryManager.isCharging());
         }
     };
 
@@ -74,23 +70,19 @@
     private AppIdleController(StateChangedListener stateChangedListener, Context context) {
         super(stateChangedListener, context);
         mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
-        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
-        mPluggedIn = isPowered();
+        mBatteryManager = context.getSystemService(BatteryManager.class);
+        mPluggedIn = mBatteryManager.isCharging();
         mUsageStatsInternal.addAppIdleStateChangeListener(this);
         registerReceivers();
     }
 
     private void registerReceivers() {
         // Monitor battery charging state
-        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING);
+        filter.addAction(BatteryManager.ACTION_DISCHARGING);
         mContext.registerReceiver(mReceiver, filter);
     }
 
-    private boolean isPowered() {
-        // TODO: Allow any charger type
-        return mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_AC);
-    }
-
     @Override
     public void maybeStartTrackingJob(JobStatus jobStatus) {
         synchronized (mTrackedTasks) {
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 834dff2..1fb22be 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -42,11 +42,13 @@
  * {@hide}
  */
 public class FlpHardwareProvider {
+    private static final int FIRST_VERSION_WITH_FLUSH_LOCATIONS = 2;
     private GeofenceHardwareImpl mGeofenceHardwareSink = null;
     private IFusedLocationHardwareSink mLocationSink = null;
     // Capabilities provided by FlpCallbacks
     private boolean mHaveBatchingCapabilities;
     private int mBatchingCapabilities;
+    private int mVersion;
 
     private static FlpHardwareProvider sSingletonInstance = null;
 
@@ -150,6 +152,11 @@
         }
     }
 
+    private void setVersion(int version) {
+        mVersion = version;
+        getGeofenceHardwareSink().setVersion(version);
+    }
+
     private void maybeSendCapabilities() {
         IFusedLocationHardwareSink sink;
         boolean haveBatchingCapabilities;
@@ -366,7 +373,12 @@
 
         @Override
         public void flushBatchedLocations() {
-            nativeFlushBatchedLocations();
+            if (mVersion >= FIRST_VERSION_WITH_FLUSH_LOCATIONS) {
+                nativeFlushBatchedLocations();
+            } else {
+                Log.wtf(TAG,
+                        "Tried to call flushBatchedLocations on an unsupported implementation");
+            }
         }
 
         @Override
@@ -388,6 +400,11 @@
         public void injectDeviceContext(int deviceEnabledContext) {
             nativeInjectDeviceContext(deviceEnabledContext);
         }
+
+        @Override
+        public int getVersion() {
+            return mVersion;
+        }
     };
 
     private final IFusedGeofenceHardware mGeofenceHardwareService =
diff --git a/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java b/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
index e49c411..a08d326 100644
--- a/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
+++ b/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
@@ -122,4 +122,10 @@
         checkPermissions();
         mLocationHardware.flushBatchedLocations();
     }
+
+    @Override
+    public int getVersion() throws RemoteException {
+        checkPermissions();
+        return mLocationHardware.getVersion();
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 683d1fd..40218bb 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -501,7 +501,7 @@
                     if (isChange && policy.doNotDisturbWhenSilent) {
                         if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS
                                 && mZenMode != Global.ZEN_MODE_ALARMS) {
-                            newZen = Global.ZEN_MODE_NO_INTERRUPTIONS;
+                            newZen = Global.ZEN_MODE_ALARMS;
                         }
                     }
                     break;
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index 852b5e9..2ca5f5a 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -31,6 +31,7 @@
 static JNIEnv *sCallbackEnv = NULL;
 static hw_device_t* sHardwareDevice = NULL;
 
+static jmethodID sSetVersion = NULL;
 static jmethodID sOnLocationReport = NULL;
 static jmethodID sOnDataReport = NULL;
 static jmethodID sOnBatchingCapabilities = NULL;
@@ -141,6 +142,14 @@
       }
 
       ALOGV("Callback thread attached: %p", sCallbackEnv);
+
+      // Send the version to the upper layer.
+      sCallbackEnv->CallVoidMethod(
+            sCallbacksObj,
+            sSetVersion,
+            sFlpInterface->size == sizeof(FlpLocationInterface) ? 2 : 1
+            );
+      CheckExceptions(sCallbackEnv, __FUNCTION__);
       break;
     }
     case DISASSOCIATE_JVM:
@@ -176,6 +185,10 @@
   sFlpInterface = NULL;
 
   // get references to the Java provider methods
+  sSetVersion = env->GetMethodID(
+        clazz,
+        "setVersion",
+        "(I)V");
   sOnLocationReport = env->GetMethodID(
       clazz,
       "onLocationReport",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c7d2b86..c5e1933 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2813,7 +2813,7 @@
         try {
             LockPatternUtils utils = new LockPatternUtils(mContext);
             if (!TextUtils.isEmpty(password)) {
-                utils.saveLockPassword(password, quality, userHandle);
+                utils.saveLockPassword(password, null, quality, userHandle);
             } else {
                 utils.clearLock(userHandle);
             }
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index bf0e75d..dae8447 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -219,31 +219,31 @@
     public void testPassword_Write() {
         mStorage.writePasswordHash("thepassword".getBytes(), 0);
 
-        assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0));
+        assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash);
         mStorage.clearCache();
-        assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0));
+        assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash);
     }
 
     public void testPassword_WriteProfileWritesParent() {
         mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
         mStorage.writePasswordHash("profilepassword".getBytes(), 2);
 
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1));
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2));
+        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash);
+        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
         mStorage.clearCache();
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1));
-        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2));
+        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash);
+        assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
     }
 
     public void testPassword_WriteParentWritesProfile() {
         mStorage.writePasswordHash("profilepassword".getBytes(), 2);
         mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
 
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1));
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2));
+        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash);
+        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash);
         mStorage.clearCache();
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1));
-        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2));
+        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash);
+        assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash);
     }
 
     public void testPattern_Default() {
@@ -253,31 +253,31 @@
     public void testPattern_Write() {
         mStorage.writePatternHash("thepattern".getBytes(), 0);
 
-        assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0));
+        assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash);
         mStorage.clearCache();
-        assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0));
+        assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash);
     }
 
     public void testPattern_WriteProfileWritesParent() {
         mStorage.writePatternHash("parentpatternn".getBytes(), 1);
         mStorage.writePatternHash("profilepattern".getBytes(), 2);
 
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1));
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2));
+        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash);
+        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash);
         mStorage.clearCache();
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1));
-        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2));
+        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash);
+        assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash);
     }
 
     public void testPattern_WriteParentWritesProfile() {
         mStorage.writePatternHash("profilepattern".getBytes(), 2);
         mStorage.writePatternHash("parentpatternn".getBytes(), 1);
 
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1));
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2));
+        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash);
+        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash);
         mStorage.clearCache();
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1));
-        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2));
+        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash);
+        assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash);
     }
 
     public void testPrefetch() {
@@ -289,8 +289,8 @@
         mStorage.prefetchUser(0);
 
         assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0));
-        assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0));
-        assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0));
+        assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0).hash);
+        assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0).hash);
     }
 
     public void testFileLocation_Owner() {