Merge "Add lockTaskOnLaunch attribute."
diff --git a/api/current.txt b/api/current.txt
index ce6bbfa..b3d5fd0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2736,6 +2736,7 @@
   }
 
   public class AccountManager {
+    method public boolean accountAuthenticated(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
@@ -2796,6 +2797,7 @@
     field public static final java.lang.String KEY_ERROR_CODE = "errorCode";
     field public static final java.lang.String KEY_ERROR_MESSAGE = "errorMessage";
     field public static final java.lang.String KEY_INTENT = "intent";
+    field public static final java.lang.String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH = "lastAuthenticatedTimeMillisEpoch";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
     field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -8377,6 +8379,7 @@
     ctor public RestrictionEntry(java.lang.String, boolean);
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
     ctor public RestrictionEntry(java.lang.String, int);
+    ctor public RestrictionEntry(java.lang.String, android.content.RestrictionEntry[], boolean);
     ctor public RestrictionEntry(android.os.Parcel);
     method public int describeContents();
     method public java.lang.String[] getAllSelectedStrings();
@@ -8385,6 +8388,7 @@
     method public java.lang.String getDescription();
     method public int getIntValue();
     method public java.lang.String getKey();
+    method public android.content.RestrictionEntry[] getRestrictions();
     method public boolean getSelectedState();
     method public java.lang.String getSelectedString();
     method public java.lang.String getTitle();
@@ -8396,6 +8400,7 @@
     method public void setChoiceValues(android.content.Context, int);
     method public void setDescription(java.lang.String);
     method public void setIntValue(int);
+    method public void setRestrictions(android.content.RestrictionEntry[]);
     method public void setSelectedState(boolean);
     method public void setSelectedString(java.lang.String);
     method public void setTitle(java.lang.String);
@@ -8403,6 +8408,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.RestrictionEntry> CREATOR;
     field public static final int TYPE_BOOLEAN = 1; // 0x1
+    field public static final int TYPE_BUNDLE = 7; // 0x7
+    field public static final int TYPE_BUNDLE_ARRAY = 8; // 0x8
     field public static final int TYPE_CHOICE = 2; // 0x2
     field public static final int TYPE_INTEGER = 5; // 0x5
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
@@ -8411,6 +8418,7 @@
   }
 
   public class RestrictionsManager {
+    method public static android.os.Bundle convertRestrictionsToBundle(java.util.List<android.content.RestrictionEntry>);
     method public android.content.Intent createLocalApprovalIntent();
     method public android.os.Bundle getApplicationRestrictions();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
@@ -11163,6 +11171,7 @@
     field public static final int NV21 = 17; // 0x11
     field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
+    field public static final int RAW12 = 38; // 0x26
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -26850,6 +26859,7 @@
     method public void ioReceive();
     method public void ioSend();
     method public deprecated synchronized void resize(int);
+    method public void setAutoPadding(boolean);
     method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
     method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
     method public void setFromFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
@@ -27497,6 +27507,41 @@
     method public android.renderscript.ScriptGroup create();
   }
 
+  public class ScriptGroup2 extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2(long, android.renderscript.RenderScript);
+    method public java.lang.Object[] execute(java.lang.Object...);
+  }
+
+  public static final class ScriptGroup2.Binding {
+    ctor public ScriptGroup2.Binding(android.renderscript.Script.FieldID, java.lang.Object);
+    method public android.renderscript.Script.FieldID getField();
+    method public java.lang.Object getValue();
+  }
+
+  public static final class ScriptGroup2.Builder {
+    ctor public ScriptGroup2.Builder(android.renderscript.RenderScript);
+    method public android.renderscript.ScriptGroup2.UnboundValue addInput();
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2 create(android.renderscript.ScriptGroup2.Future...);
+  }
+
+  public static class ScriptGroup2.Closure extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2.Closure(long, android.renderscript.RenderScript);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Future getGlobal(android.renderscript.Script.FieldID);
+    method public android.renderscript.ScriptGroup2.Future getReturn();
+  }
+
+  public static class ScriptGroup2.Future {
+  }
+
+  public static class ScriptGroup2.UnboundValue {
+  }
+
   public abstract class ScriptIntrinsic extends android.renderscript.Script {
   }
 
@@ -34289,8 +34334,10 @@
     method public long getTimeDelta();
     method public boolean isInProgress();
     method public boolean isQuickScaleEnabled();
+    method public boolean isSecondaryButtonScaleEnabled();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setQuickScaleEnabled(boolean);
+    method public void setSecondaryButtonScaleEnabled(boolean);
   }
 
   public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index 6053603..9462054 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2815,6 +2815,7 @@
   }
 
   public class AccountManager {
+    method public boolean accountAuthenticated(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
@@ -2875,6 +2876,7 @@
     field public static final java.lang.String KEY_ERROR_CODE = "errorCode";
     field public static final java.lang.String KEY_ERROR_MESSAGE = "errorMessage";
     field public static final java.lang.String KEY_INTENT = "intent";
+    field public static final java.lang.String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH = "lastAuthenticatedTimeMillisEpoch";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
     field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -8598,6 +8600,7 @@
     ctor public RestrictionEntry(java.lang.String, boolean);
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
     ctor public RestrictionEntry(java.lang.String, int);
+    ctor public RestrictionEntry(java.lang.String, android.content.RestrictionEntry[], boolean);
     ctor public RestrictionEntry(android.os.Parcel);
     method public int describeContents();
     method public java.lang.String[] getAllSelectedStrings();
@@ -8606,6 +8609,7 @@
     method public java.lang.String getDescription();
     method public int getIntValue();
     method public java.lang.String getKey();
+    method public android.content.RestrictionEntry[] getRestrictions();
     method public boolean getSelectedState();
     method public java.lang.String getSelectedString();
     method public java.lang.String getTitle();
@@ -8617,6 +8621,7 @@
     method public void setChoiceValues(android.content.Context, int);
     method public void setDescription(java.lang.String);
     method public void setIntValue(int);
+    method public void setRestrictions(android.content.RestrictionEntry[]);
     method public void setSelectedState(boolean);
     method public void setSelectedString(java.lang.String);
     method public void setTitle(java.lang.String);
@@ -8624,6 +8629,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.RestrictionEntry> CREATOR;
     field public static final int TYPE_BOOLEAN = 1; // 0x1
+    field public static final int TYPE_BUNDLE = 7; // 0x7
+    field public static final int TYPE_BUNDLE_ARRAY = 8; // 0x8
     field public static final int TYPE_CHOICE = 2; // 0x2
     field public static final int TYPE_INTEGER = 5; // 0x5
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
@@ -8632,6 +8639,7 @@
   }
 
   public class RestrictionsManager {
+    method public static android.os.Bundle convertRestrictionsToBundle(java.util.List<android.content.RestrictionEntry>);
     method public android.content.Intent createLocalApprovalIntent();
     method public android.os.Bundle getApplicationRestrictions();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
@@ -11449,6 +11457,7 @@
     field public static final int NV21 = 17; // 0x11
     field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
+    field public static final int RAW12 = 38; // 0x26
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -28829,6 +28838,7 @@
     method public void ioReceive();
     method public void ioSend();
     method public deprecated synchronized void resize(int);
+    method public void setAutoPadding(boolean);
     method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
     method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
     method public void setFromFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
@@ -29476,6 +29486,41 @@
     method public android.renderscript.ScriptGroup create();
   }
 
+  public class ScriptGroup2 extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2(long, android.renderscript.RenderScript);
+    method public java.lang.Object[] execute(java.lang.Object...);
+  }
+
+  public static final class ScriptGroup2.Binding {
+    ctor public ScriptGroup2.Binding(android.renderscript.Script.FieldID, java.lang.Object);
+    method public android.renderscript.Script.FieldID getField();
+    method public java.lang.Object getValue();
+  }
+
+  public static final class ScriptGroup2.Builder {
+    ctor public ScriptGroup2.Builder(android.renderscript.RenderScript);
+    method public android.renderscript.ScriptGroup2.UnboundValue addInput();
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2 create(android.renderscript.ScriptGroup2.Future...);
+  }
+
+  public static class ScriptGroup2.Closure extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2.Closure(long, android.renderscript.RenderScript);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Future getGlobal(android.renderscript.Script.FieldID);
+    method public android.renderscript.ScriptGroup2.Future getReturn();
+  }
+
+  public static class ScriptGroup2.Future {
+  }
+
+  public static class ScriptGroup2.UnboundValue {
+  }
+
   public abstract class ScriptIntrinsic extends android.renderscript.Script {
   }
 
@@ -36831,8 +36876,10 @@
     method public long getTimeDelta();
     method public boolean isInProgress();
     method public boolean isQuickScaleEnabled();
+    method public boolean isSecondaryButtonScaleEnabled();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setQuickScaleEnabled(boolean);
+    method public void setSecondaryButtonScaleEnabled(boolean);
   }
 
   public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 6957435c..480d171 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -203,6 +203,14 @@
     public static final String KEY_USERDATA = "userdata";
 
     /**
+     * Bundle key used to supply the last time the credentials of the account
+     * were authenticated successfully. Time is specified in milliseconds since
+     * epoch.
+     */
+    public static final String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH =
+            "lastAuthenticatedTimeMillisEpoch";
+
+    /**
      * Authenticators using 'customTokens' option will also get the UID of the
      * caller
      */
@@ -663,6 +671,31 @@
     }
 
     /**
+     * Informs the system that the account has been authenticated recently. This
+     * recency may be used by other applications to verify the account. This
+     * should be called only when the user has entered correct credentials for
+     * the account.
+     * <p>
+     * It is not safe to call this method from the main thread. As such, call it
+     * from another thread.
+     * <p>
+     * This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and should be
+     * called from the account's authenticator.
+     *
+     * @param account The {@link Account} to be updated.
+     */
+    public boolean accountAuthenticated(Account account) {
+        if (account == null)
+            throw new IllegalArgumentException("account is null");
+        try {
+            return mService.accountAuthenticated(account);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Rename the specified {@link Account}.  This is equivalent to removing
      * the existing account and adding a new renamed account with the old
      * account's user data.
@@ -1544,15 +1577,20 @@
      *     with these fields if activity or password was supplied and
      *     the account was successfully verified:
      * <ul>
-     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account verified
      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
      * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success
      * </ul>
      *
      * If no activity or password was specified, the returned Bundle contains
-     * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
-     * password prompt.  If an error occurred,
-     * {@link AccountManagerFuture#getResult()} throws:
+     * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+     * password prompt.
+     * 
+     * <p>Also the returning Bundle may contain {@link
+     * #KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH} indicating the last time the
+     * credential was validated/created.
+     * 
+     * If an error occurred,{@link AccountManagerFuture#getResult()} throws:
      * <ul>
      * <li> {@link AuthenticatorException} if the authenticator failed to respond
      * <li> {@link OperationCanceledException} if the operation was canceled for
@@ -1625,9 +1663,9 @@
      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
      * </ul>
      *
-     * If no activity was specified, the returned Bundle contains only
+     * If no activity was specified, the returned Bundle contains
      * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
-     * password prompt.  If an error occurred,
+     * password prompt. If an error occurred,
      * {@link AccountManagerFuture#getResult()} throws:
      * <ul>
      * <li> {@link AuthenticatorException} if the authenticator failed to respond
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index aa41161..04b3c88 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -67,6 +67,7 @@
         boolean expectActivityLaunch);
     void confirmCredentialsAsUser(in IAccountManagerResponse response, in Account account,
         in Bundle options, boolean expectActivityLaunch, int userId);
+    boolean accountAuthenticated(in Account account);
     void getAuthTokenLabel(in IAccountManagerResponse response, String accountType,
         String authTokenType);
 
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 6d79626..342ee38 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -20,6 +20,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Applications can expose restrictions for a restricted user on a
  * multiuser device. The administrator can configure these restrictions that will then be
@@ -33,19 +36,19 @@
 public class RestrictionEntry implements Parcelable {
 
     /**
-     * A type of restriction. Use this type for information that needs to be transferred across
-     * but shouldn't be presented to the user in the UI. Stores a single String value.
+     * Hidden restriction type. Use this type for information that needs to be transferred
+     * across but shouldn't be presented to the user in the UI. Stores a single String value.
      */
     public static final int TYPE_NULL         = 0;
 
     /**
-     * A type of restriction. Use this for storing a boolean value, typically presented as
+     * Restriction of type "bool". Use this for storing a boolean value, typically presented as
      * a checkbox in the UI.
      */
     public static final int TYPE_BOOLEAN      = 1;
 
     /**
-     * A type of restriction. Use this for storing a string value, typically presented as
+     * Restriction of type "choice". Use this for storing a string value, typically presented as
      * a single-select list. Call {@link #setChoiceEntries(String[])} and
      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
      * and the corresponding values, respectively.
@@ -53,7 +56,7 @@
     public static final int TYPE_CHOICE       = 2;
 
     /**
-     * A type of restriction. Use this for storing a string value, typically presented as
+     * Internal restriction type. Use this for storing a string value, typically presented as
      * a single-select list. Call {@link #setChoiceEntries(String[])} and
      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
      * and the corresponding values, respectively.
@@ -64,8 +67,8 @@
     public static final int TYPE_CHOICE_LEVEL = 3;
 
     /**
-     * A type of restriction. Use this for presenting a multi-select list where more than one
-     * entry can be selected, such as for choosing specific titles to white-list.
+     * Restriction of type "multi-select". Use this for presenting a multi-select list where more
+     * than one entry can be selected, such as for choosing specific titles to white-list.
      * Call {@link #setChoiceEntries(String[])} and
      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
      * and the corresponding values, respectively.
@@ -75,18 +78,30 @@
     public static final int TYPE_MULTI_SELECT = 4;
 
     /**
-     * A type of restriction. Use this for storing an integer value. The range of values
+     * Restriction of type "integer". Use this for storing an integer value. The range of values
      * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
      */
     public static final int TYPE_INTEGER = 5;
 
     /**
-     * A type of restriction. Use this for storing a string value.
+     * Restriction of type "string". Use this for storing a string value.
      * @see #setSelectedString
      * @see #getSelectedString
      */
     public static final int TYPE_STRING = 6;
 
+    /**
+     * Restriction of type "bundle". Use this for storing {@link android.os.Bundle bundles} of
+     * restrictions
+     */
+    public static final int TYPE_BUNDLE = 7;
+
+    /**
+     * Restriction of type "bundle_array". Use this for storing arrays of
+     * {@link android.os.Bundle bundles} of restrictions
+     */
+    public static final int TYPE_BUNDLE_ARRAY = 8;
+
     /** The type of restriction. */
     private int mType;
 
@@ -100,13 +115,13 @@
     private String mDescription;
 
     /** The user-visible set of choices used for single-select and multi-select lists. */
-    private String [] mChoiceEntries;
+    private String[] mChoiceEntries;
 
     /** The values corresponding to the user-visible choices. The value(s) of this entry will
      * one or more of these, returned by {@link #getAllSelectedStrings()} and
      * {@link #getSelectedString()}.
      */
-    private String [] mChoiceValues;
+    private String[] mChoiceValues;
 
     /* The chosen value, whose content depends on the type of the restriction. */
     private String mCurrentValue;
@@ -115,6 +130,12 @@
     private String[] mCurrentValues;
 
     /**
+     * List of nested restrictions. Used by {@link #TYPE_BUNDLE bundle} and
+     * {@link #TYPE_BUNDLE_ARRAY bundle_array} restrictions.
+     */
+    private RestrictionEntry[] mRestrictions;
+
+    /**
      * Constructor for specifying the type and key, with no initial value;
      *
      * @param type the restriction type.
@@ -170,6 +191,35 @@
     }
 
     /**
+     * Constructor for {@link #TYPE_BUNDLE}/{@link #TYPE_BUNDLE_ARRAY} type.
+     * @param key the unique key for this restriction
+     * @param restrictionEntries array of nested restriction entries. If the entry, being created
+     * represents a {@link #TYPE_BUNDLE_ARRAY bundle-array}, {@code restrictionEntries} array may
+     * only contain elements of type {@link #TYPE_BUNDLE bundle}.
+     * @param isBundleArray true if this restriction represents
+     * {@link #TYPE_BUNDLE_ARRAY bundle-array} type, otherwise the type will be set to
+     * {@link #TYPE_BUNDLE bundle}.
+     */
+    public RestrictionEntry(String key, RestrictionEntry[] restrictionEntries,
+            boolean isBundleArray) {
+        mKey = key;
+        if (isBundleArray) {
+            mType = TYPE_BUNDLE_ARRAY;
+            if (restrictionEntries != null) {
+                for (RestrictionEntry restriction : restrictionEntries) {
+                    if (restriction.getType() != TYPE_BUNDLE) {
+                        throw new IllegalArgumentException("bundle_array restriction can only have "
+                                + "nested restriction entries of type bundle");
+                    }
+                }
+            }
+        } else {
+            mType = TYPE_BUNDLE;
+        }
+        setRestrictions(restrictionEntries);
+    }
+
+    /**
      * Sets the type for this restriction.
      * @param type the type for this restriction.
      */
@@ -283,6 +333,22 @@
     }
 
     /**
+     * Returns array of possible restriction entries that this entry may contain.
+     */
+    public RestrictionEntry[] getRestrictions() {
+        return mRestrictions;
+    }
+
+   /**
+    * Sets an array of possible restriction entries, that this entry may contain.
+    * <p>This method is only relevant for types {@link #TYPE_BUNDLE} and
+    * {@link #TYPE_BUNDLE_ARRAY}
+    */
+    public void setRestrictions(RestrictionEntry[] restrictions) {
+        mRestrictions = restrictions;
+    }
+
+    /**
      * Returns the list of possible string values set earlier.
      * @return the list of possible values.
      */
@@ -362,27 +428,30 @@
         this.mTitle = title;
     }
 
-    private boolean equalArrays(String[] one, String[] other) {
-        if (one.length != other.length) return false;
-        for (int i = 0; i < one.length; i++) {
-            if (!one[i].equals(other[i])) return false;
-        }
-        return true;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
         if (!(o instanceof RestrictionEntry)) return false;
         final RestrictionEntry other = (RestrictionEntry) o;
-        // Make sure that either currentValue matches or currentValues matches.
-        return mType == other.mType && mKey.equals(other.mKey)
-                &&
-                ((mCurrentValues == null && other.mCurrentValues == null
-                  && mCurrentValue != null && mCurrentValue.equals(other.mCurrentValue))
-                 ||
-                 (mCurrentValue == null && other.mCurrentValue == null
-                  && mCurrentValues != null && equalArrays(mCurrentValues, other.mCurrentValues)));
+        if (mType != other.mType || mKey.equals(other.mKey)) {
+            return false;
+        }
+        if (mCurrentValues == null && other.mCurrentValues == null
+                && mRestrictions == null && other.mRestrictions == null
+                && Objects.equals(mCurrentValue, other.mCurrentValue)) {
+            return true;
+        }
+        if (mCurrentValue == null && other.mCurrentValue == null
+                && mRestrictions == null && other.mRestrictions == null
+                && Arrays.equals(mCurrentValues, other.mCurrentValues)) {
+            return true;
+        }
+        if (mCurrentValue == null && other.mCurrentValue == null
+                && mCurrentValue == null && other.mCurrentValue == null
+                && Arrays.equals(mRestrictions, other.mRestrictions)) {
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -397,28 +466,28 @@
                     result = 31 * result + value.hashCode();
                 }
             }
+        } else if (mRestrictions != null) {
+            result = 31 * result + Arrays.hashCode(mRestrictions);
         }
         return result;
     }
 
-    private String[] readArray(Parcel in) {
-        int count = in.readInt();
-        String[] values = new String[count];
-        for (int i = 0; i < count; i++) {
-            values[i] = in.readString();
-        }
-        return values;
-    }
-
     public RestrictionEntry(Parcel in) {
         mType = in.readInt();
         mKey = in.readString();
         mTitle = in.readString();
         mDescription = in.readString();
-        mChoiceEntries = readArray(in);
-        mChoiceValues = readArray(in);
+        mChoiceEntries = in.readStringArray();
+        mChoiceValues = in.readStringArray();
         mCurrentValue = in.readString();
-        mCurrentValues = readArray(in);
+        mCurrentValues = in.readStringArray();
+        Parcelable[] parcelables = in.readParcelableArray(null);
+        if (parcelables != null) {
+            mRestrictions = new RestrictionEntry[parcelables.length];
+            for (int i = 0; i < parcelables.length; i++) {
+                mRestrictions[i] = (RestrictionEntry) parcelables[i];
+            }
+        }
     }
 
     @Override
@@ -426,27 +495,17 @@
         return 0;
     }
 
-    private void writeArray(Parcel dest, String[] values) {
-        if (values == null) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(values.length);
-            for (int i = 0; i < values.length; i++) {
-                dest.writeString(values[i]);
-            }
-        }
-    }
-
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
         dest.writeString(mKey);
         dest.writeString(mTitle);
         dest.writeString(mDescription);
-        writeArray(dest, mChoiceEntries);
-        writeArray(dest, mChoiceValues);
+        dest.writeStringArray(mChoiceEntries);
+        dest.writeStringArray(mChoiceValues);
         dest.writeString(mCurrentValue);
-        writeArray(dest, mCurrentValues);
+        dest.writeStringArray(mCurrentValues);
+        dest.writeParcelableArray(mRestrictions, 0);
     }
 
     public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
@@ -461,6 +520,16 @@
 
     @Override
     public String toString() {
-        return "RestrictionsEntry {type=" + mType + ", key=" + mKey + ", value=" + mCurrentValue + "}";
+        return "RestrictionEntry{" +
+                "mType=" + mType +
+                ", mKey='" + mKey + '\'' +
+                ", mTitle='" + mTitle + '\'' +
+                ", mDescription='" + mDescription + '\'' +
+                ", mChoiceEntries=" + Arrays.toString(mChoiceEntries) +
+                ", mChoiceValues=" + Arrays.toString(mChoiceValues) +
+                ", mCurrentValue='" + mCurrentValue + '\'' +
+                ", mCurrentValues=" + Arrays.toString(mCurrentValues) +
+                ", mRestrictions=" + Arrays.toString(mRestrictions) +
+                '}';
     }
 }
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 21a6a0d..1fac06e 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -32,12 +32,14 @@
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -71,12 +73,15 @@
  *         android:key="string"
  *         android:title="string resource"
  *         android:restrictionType=["bool" | "string" | "integer"
- *                                         | "choice" | "multi-select" | "hidden"]
+ *                                         | "choice" | "multi-select" | "hidden"
+ *                                         | "bundle" | "bundle_array"]
  *         android:description="string resource"
  *         android:entries="string-array resource"
  *         android:entryValues="string-array resource"
- *         android:defaultValue="reference"
- *         /&gt;
+ *         android:defaultValue="reference" &gt;
+ *             &lt;restriction ... /&gt;
+ *             ...
+ *     &lt;/restriction&gt;
  *     &lt;restriction ... /&gt;
  *     ...
  * &lt;/restrictions&gt;
@@ -97,6 +102,9 @@
  * administrator controlling the values, if the title is not sufficient.</li>
  * </ul>
  * <p>
+ * Only restrictions of type {@code bundle} and {@code bundle_array} can have one or multiple nested
+ * restriction elements.
+ * <p>
  * In your manifest's <code>application</code> section, add the meta-data tag to point to
  * the restrictions XML file as shown below:
  * <pre>
@@ -537,9 +545,7 @@
 
         XmlResourceParser xml =
                 appInfo.loadXmlMetaData(mContext.getPackageManager(), META_DATA_APP_RESTRICTIONS);
-        List<RestrictionEntry> restrictions = loadManifestRestrictions(packageName, xml);
-
-        return restrictions;
+        return loadManifestRestrictions(packageName, xml);
     }
 
     private List<RestrictionEntry> loadManifestRestrictions(String packageName,
@@ -550,23 +556,16 @@
         } catch (NameNotFoundException nnfe) {
             return null;
         }
-        ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>();
+        ArrayList<RestrictionEntry> restrictions = new ArrayList<>();
         RestrictionEntry restriction;
 
         try {
             int tagType = xml.next();
             while (tagType != XmlPullParser.END_DOCUMENT) {
                 if (tagType == XmlPullParser.START_TAG) {
-                    if (xml.getName().equals(TAG_RESTRICTION)) {
-                        AttributeSet attrSet = Xml.asAttributeSet(xml);
-                        if (attrSet != null) {
-                            TypedArray a = appContext.obtainStyledAttributes(attrSet,
-                                    com.android.internal.R.styleable.RestrictionEntry);
-                            restriction = loadRestriction(appContext, a);
-                            if (restriction != null) {
-                                restrictions.add(restriction);
-                            }
-                        }
+                    restriction = loadRestrictionElement(appContext, xml);
+                    if (restriction != null) {
+                        restrictions.add(restriction);
                     }
                 }
                 tagType = xml.next();
@@ -582,7 +581,21 @@
         return restrictions;
     }
 
-    private RestrictionEntry loadRestriction(Context appContext, TypedArray a) {
+    private RestrictionEntry loadRestrictionElement(Context appContext, XmlResourceParser xml)
+            throws IOException, XmlPullParserException {
+        if (xml.getName().equals(TAG_RESTRICTION)) {
+            AttributeSet attrSet = Xml.asAttributeSet(xml);
+            if (attrSet != null) {
+                TypedArray a = appContext.obtainStyledAttributes(attrSet,
+                        com.android.internal.R.styleable.RestrictionEntry);
+                return loadRestriction(appContext, a, xml);
+            }
+        }
+        return null;
+    }
+
+    private RestrictionEntry loadRestriction(Context appContext, TypedArray a, XmlResourceParser xml)
+            throws IOException, XmlPullParserException {
         String key = a.getString(R.styleable.RestrictionEntry_key);
         int restrictionType = a.getInt(
                 R.styleable.RestrictionEntry_restrictionType, -1);
@@ -633,9 +646,90 @@
                 restriction.setSelectedState(
                         a.getBoolean(R.styleable.RestrictionEntry_defaultValue, false));
                 break;
+            case RestrictionEntry.TYPE_BUNDLE:
+            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
+                final int outerDepth = xml.getDepth();
+                List<RestrictionEntry> restrictionEntries = new ArrayList<>();
+                while (XmlUtils.nextElementWithin(xml, outerDepth)) {
+                    RestrictionEntry childEntry = loadRestrictionElement(appContext, xml);
+                    if (childEntry == null) {
+                        Log.w(TAG, "Child entry cannot be loaded for bundle restriction " + key);
+                    } else {
+                        restrictionEntries.add(childEntry);
+                        if (restrictionType == RestrictionEntry.TYPE_BUNDLE_ARRAY
+                                && childEntry.getType() != RestrictionEntry.TYPE_BUNDLE) {
+                            Log.w(TAG, "bundle_array " + key
+                                    + " can only contain entries of type bundle");
+                        }
+                    }
+                }
+                restriction.setRestrictions(restrictionEntries.toArray(new RestrictionEntry[
+                        restrictionEntries.size()]));
+                break;
             default:
                 Log.w(TAG, "Unknown restriction type " + restrictionType);
         }
         return restriction;
     }
+
+    /**
+     * Converts a list of restrictions to the corresponding bundle, using the following mapping:
+     * <table>
+     *     <tr><th>RestrictionEntry</th><th>Bundle</th></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BOOLEAN}</td><td>{@link Bundle#putBoolean}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_CHOICE}, {@link RestrictionEntry#TYPE_CHOICE}</td>
+     *     <td>{@link Bundle#putStringArray}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_INTEGER}</td><td>{@link Bundle#putInt}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_STRING}</td><td>{@link Bundle#putString}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE}</td><td>{@link Bundle#putBundle}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE_ARRAY}</td>
+     *     <td>{@link Bundle#putParcelableArray}</td></tr>
+     * </table>
+     * @param entries list of restrictions
+     */
+    public static Bundle convertRestrictionsToBundle(List<RestrictionEntry> entries) {
+        final Bundle bundle = new Bundle();
+        for (RestrictionEntry entry : entries) {
+            addRestrictionToBundle(bundle, entry);
+        }
+        return bundle;
+    }
+
+    private static Bundle addRestrictionToBundle(Bundle bundle, RestrictionEntry entry) {
+        switch (entry.getType()) {
+            case RestrictionEntry.TYPE_BOOLEAN:
+                bundle.putBoolean(entry.getKey(), entry.getSelectedState());
+                break;
+            case RestrictionEntry.TYPE_CHOICE:
+            case RestrictionEntry.TYPE_CHOICE_LEVEL:
+            case RestrictionEntry.TYPE_MULTI_SELECT:
+                bundle.putStringArray(entry.getKey(), entry.getAllSelectedStrings());
+                break;
+            case RestrictionEntry.TYPE_INTEGER:
+                bundle.putInt(entry.getKey(), entry.getIntValue());
+                break;
+            case RestrictionEntry.TYPE_STRING:
+            case RestrictionEntry.TYPE_NULL:
+                bundle.putString(entry.getKey(), entry.getSelectedString());
+                break;
+            case RestrictionEntry.TYPE_BUNDLE:
+                RestrictionEntry[] restrictions = entry.getRestrictions();
+                Bundle childBundle = convertRestrictionsToBundle(Arrays.asList(restrictions));
+                bundle.putBundle(entry.getKey(), childBundle);
+                break;
+            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
+                restrictions = entry.getRestrictions();
+                Bundle[] bundleArray = new Bundle[restrictions.length];
+                for (int i = 0; i < restrictions.length; i++) {
+                    bundleArray[i] = addRestrictionToBundle(new Bundle(), restrictions[i]);
+                }
+                bundle.putParcelableArray(entry.getKey(), bundleArray);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unsupported restrictionEntry type: " + entry.getType());
+        }
+        return bundle;
+    }
+
 }
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index d5dfaf6..9bc2f46 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -75,4 +75,11 @@
                     out BinderHolder device);
 
     int setTorchMode(String CameraId, boolean enabled, IBinder clientBinder);
+
+    /**
+     * Notify the camera service of a system event.  Should only be called from system_server.
+     *
+     * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
+     */
+    oneway void notifySystemEvent(int eventId, int arg0);
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a0217c2..1503bf5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -991,7 +991,8 @@
      * <ul>
      * <li>Processed (but stalling): any non-RAW format with a stallDurations &gt; 0.
      * Typically JPEG format (ImageFormat#JPEG).</li>
-     * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10 and ImageFormat#RAW_OPAQUE.</li>
+     * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10, ImageFormat#RAW12,
+     * and ImageFormat#RAW_OPAQUE.</li>
      * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
      * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
      * </ul>
@@ -1023,6 +1024,7 @@
      * <ul>
      * <li>ImageFormat#RAW_SENSOR</li>
      * <li>ImageFormat#RAW10</li>
+     * <li>ImageFormat#RAW12</li>
      * <li>Opaque <code>RAW</code></li>
      * </ul>
      * <p>LEGACY mode devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> LEGACY)
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a0e2bf8..3abccbc 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -40,6 +40,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.net.VpnConfig;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Protocol;
@@ -2447,6 +2448,38 @@
     }
 
     /**
+     * Resets all connectivity manager settings back to factory defaults.
+     * @hide
+     */
+    public void factoryReset() {
+        // Turn airplane mode off
+        setAirplaneMode(false);
+
+        // Untether
+        for (String tether : getTetheredIfaces()) {
+            untether(tether);
+        }
+
+        // Turn VPN off
+        try {
+            VpnConfig vpnConfig = mService.getVpnConfig();
+            if (vpnConfig != null) {
+                if (vpnConfig.legacy) {
+                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+                } else {
+                    // Prevent this app from initiating VPN connections in the future without
+                    // user intervention.
+                    mService.setVpnPackageAuthorization(false);
+
+                    mService.prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+                }
+            }
+        } catch (RemoteException e) {
+            // Well, we tried
+        }
+    }
+
+    /**
      * Binds the current process to {@code network}.  All Sockets created in the future
      * (and not explicitly bound via a bound SocketFactory from
      * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index a8e7757..a7ffee9 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -180,6 +180,33 @@
     }
 
     /**
+     * Resets network policy settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset(String subscriber) {
+        // Turn mobile data limit off
+        NetworkPolicy[] policies = getNetworkPolicies();
+        NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriber);
+        for (NetworkPolicy policy : policies) {
+            if (policy.template.equals(template)) {
+                policy.limitBytes = NetworkPolicy.LIMIT_DISABLED;
+                policy.inferred = false;
+                policy.clearSnooze();
+            }
+        }
+        setNetworkPolicies(policies);
+
+        // Turn restrict background data off
+        setRestrictBackground(false);
+
+        // Remove app's "restrict background data" flag
+        for (int uid : getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
+            setUidPolicy(uid, NetworkPolicyManager.POLICY_NONE);
+        }
+    }
+
+    /**
      * Compute the last cycle boundary for the given {@link NetworkPolicy}. For
      * example, if cycle day is 20th, and today is June 15th, it will return May
      * 20th. When cycle day doesn't exist in current month, it snaps to the 1st
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 6517f35..7d57233 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -454,7 +454,6 @@
                 long start, int duration, Long dataUsage, boolean addForAllUsers) {
             final ContentResolver resolver = context.getContentResolver();
             int numberPresentation = PRESENTATION_ALLOWED;
-            boolean isHidden = false;
 
             TelecomManager tm = null;
             try {
@@ -469,12 +468,6 @@
                     if (address != null) {
                         accountAddress = address.getSchemeSpecificPart();
                     }
-                } else {
-                    // We could not find the account through telecom. For call log entries that
-                    // are added with a phone account which is not registered, we automatically
-                    // mark them as hidden. They are unhidden once the account is registered.
-                    Log.i(LOG_TAG, "Marking call log entry as hidden.");
-                    isHidden = true;
                 }
             }
 
@@ -520,7 +513,6 @@
             values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
             values.put(PHONE_ACCOUNT_ID, accountId);
             values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
-            values.put(PHONE_ACCOUNT_HIDDEN, Integer.valueOf(isHidden ? 1 : 0));
             values.put(NEW, Integer.valueOf(1));
 
             if (callType == MISSED_TYPE) {
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index 4fc9d24..9b46ad3 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -28,6 +28,7 @@
 public class OperationResult implements Parcelable {
     public final int resultCode;
     public final IBinder token;
+    public final long operationHandle;
     public final int inputConsumed;
     public final byte[] output;
 
@@ -45,6 +46,7 @@
     protected OperationResult(Parcel in) {
         resultCode = in.readInt();
         token = in.readStrongBinder();
+        operationHandle = in.readLong();
         inputConsumed = in.readInt();
         output = in.createByteArray();
     }
@@ -58,6 +60,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(resultCode);
         out.writeStrongBinder(token);
+        out.writeLong(operationHandle);
         out.writeInt(inputConsumed);
         out.writeByteArray(output);
     }
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 6508cca..5cf2c5c 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -130,6 +130,7 @@
     private float mFocusY;
 
     private boolean mQuickScaleEnabled;
+    private boolean mButtonScaleEnabled;
 
     private float mCurrSpan;
     private float mPrevSpan;
@@ -151,14 +152,17 @@
     private int mTouchHistoryDirection;
     private long mTouchHistoryLastAcceptedTime;
     private int mTouchMinMajor;
-    private MotionEvent mDoubleTapEvent;
-    private int mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
     private final Handler mHandler;
 
+    private float mAnchoredScaleStartX;
+    private float mAnchoredScaleStartY;
+    private int mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
+
     private static final long TOUCH_STABILIZE_TIME = 128; // ms
-    private static final int DOUBLE_TAP_MODE_NONE = 0;
-    private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
     private static final float SCALE_FACTOR = .5f;
+    private static final int ANCHORED_SCALE_MODE_NONE = 0;
+    private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1;
+    private static final int ANCHORED_SCALE_MODE_BUTTON = 2;
 
 
     /**
@@ -310,8 +314,17 @@
             mGestureDetector.onTouchEvent(event);
         }
 
+        final int count = event.getPointerCount();
+        final int toolType = event.getToolType(0);
+        final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS
+                || toolType == MotionEvent.TOOL_TYPE_MOUSE;
+        final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1)
+                && (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
+
+        final boolean anchoredScaleCancelled =
+                mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown;
         final boolean streamComplete = action == MotionEvent.ACTION_UP ||
-                action == MotionEvent.ACTION_CANCEL;
+                action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
 
         if (action == MotionEvent.ACTION_DOWN || streamComplete) {
             // Reset any scale in progress with the listener.
@@ -321,11 +334,11 @@
                 mListener.onScaleEnd(this);
                 mInProgress = false;
                 mInitialSpan = 0;
-                mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
-            } else if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS && streamComplete) {
+                mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
+            } else if (inAnchoredScaleMode() && streamComplete) {
                 mInProgress = false;
                 mInitialSpan = 0;
-                mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
+                mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
             }
 
             if (streamComplete) {
@@ -334,25 +347,32 @@
             }
         }
 
+        if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode()
+                && !streamComplete && isAnchoredScaleButtonDown) {
+            // Start of a button scale gesture
+            mAnchoredScaleStartX = event.getX();
+            mAnchoredScaleStartY = event.getY();
+            mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON;
+            mInitialSpan = 0;
+        }
+
         final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
                 action == MotionEvent.ACTION_POINTER_UP ||
-                action == MotionEvent.ACTION_POINTER_DOWN;
-
+                action == MotionEvent.ACTION_POINTER_DOWN || anchoredScaleCancelled;
 
         final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
         final int skipIndex = pointerUp ? event.getActionIndex() : -1;
 
         // Determine focal point
         float sumX = 0, sumY = 0;
-        final int count = event.getPointerCount();
         final int div = pointerUp ? count - 1 : count;
         final float focusX;
         final float focusY;
-        if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS) {
-            // In double tap mode, the focal pt is always where the double tap
-            // gesture started
-            focusX = mDoubleTapEvent.getX();
-            focusY = mDoubleTapEvent.getY();
+        if (inAnchoredScaleMode()) {
+            // In anchored scale mode, the focal pt is always where the double tap
+            // or button down gesture started
+            focusX = mAnchoredScaleStartX;
+            focusY = mAnchoredScaleStartY;
             if (event.getY() < focusY) {
                 mEventBeforeOrAboveStartingGestureEvent = true;
             } else {
@@ -390,7 +410,7 @@
         final float spanX = devX * 2;
         final float spanY = devY * 2;
         final float span;
-        if (inDoubleTapMode()) {
+        if (inAnchoredScaleMode()) {
             span = spanY;
         } else {
             span = (float) Math.hypot(spanX, spanY);
@@ -402,11 +422,10 @@
         final boolean wasInProgress = mInProgress;
         mFocusX = focusX;
         mFocusY = focusY;
-        if (!inDoubleTapMode() && mInProgress && (span < mMinSpan || configChanged)) {
+        if (!inAnchoredScaleMode() && mInProgress && (span < mMinSpan || configChanged)) {
             mListener.onScaleEnd(this);
             mInProgress = false;
             mInitialSpan = span;
-            mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
         }
         if (configChanged) {
             mPrevSpanX = mCurrSpanX = spanX;
@@ -414,7 +433,7 @@
             mInitialSpan = mPrevSpan = mCurrSpan = span;
         }
 
-        final int minSpan = inDoubleTapMode() ? mSpanSlop : mMinSpan;
+        final int minSpan = inAnchoredScaleMode() ? mSpanSlop : mMinSpan;
         if (!mInProgress && span >=  minSpan &&
                 (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
             mPrevSpanX = mCurrSpanX = spanX;
@@ -447,9 +466,8 @@
         return true;
     }
 
-
-    private boolean inDoubleTapMode() {
-        return mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS;
+    private boolean inAnchoredScaleMode() {
+        return mAnchoredScaleMode != ANCHORED_SCALE_MODE_NONE;
     }
 
     /**
@@ -466,8 +484,9 @@
                         @Override
                         public boolean onDoubleTap(MotionEvent e) {
                             // Double tap: start watching for a swipe
-                            mDoubleTapEvent = e;
-                            mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
+                            mAnchoredScaleStartX = e.getX();
+                            mAnchoredScaleStartY = e.getY();
+                            mAnchoredScaleMode = ANCHORED_SCALE_MODE_DOUBLE_TAP;
                             return true;
                         }
                     };
@@ -484,6 +503,27 @@
     }
 
     /**
+     * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks
+     * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus
+     * first button) and drags the pointer on the screen. Note that this is enabled by default if
+     * the app targets API 23 and newer.
+     *
+     * @param scales true to enable stylus or mouse scaling, false to disable.
+     */
+    public void setSecondaryButtonScaleEnabled(boolean scales) {
+        mButtonScaleEnabled = scales;
+    }
+
+    /**
+     * Return whether the button scale gesture, in which the user presses a
+     * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the
+     * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}.
+     */
+    public boolean isSecondaryButtonScaleEnabled() {
+        return mButtonScaleEnabled;
+    }
+
+    /**
      * Returns {@code true} if a scale gesture is in progress.
      */
     public boolean isInProgress() {
@@ -586,7 +626,7 @@
      * @return The current scaling factor.
      */
     public float getScaleFactor() {
-        if (inDoubleTapMode()) {
+        if (inAnchoredScaleMode()) {
             // Drag is moving up; the further away from the gesture
             // start, the smaller the span should be, the closer,
             // the larger the span, and therefore the larger the scale
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 82e36c3..32b99a8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2954,8 +2954,11 @@
             }
             mSelectionHandleHeight = Math.max(
                     mSelectHandleLeft.getMinimumHeight(), mSelectHandleRight.getMinimumHeight());
-            getInsertionController().getHandle();
-            mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight();
+            InsertionPointCursorController insertionController = getInsertionController();
+            if (insertionController != null) {
+                insertionController.getHandle();
+                mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight();
+            }
         }
 
         @Override
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index c020020..87c58d6 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -48,11 +48,6 @@
 static jclass gLineBreaks_class;
 static JLineBreaksID gLineBreaks_fieldID;
 
-static const int CHAR_SPACE = 0x20;
-static const int CHAR_TAB = 0x09;
-static const int CHAR_NEWLINE = 0x0a;
-static const int CHAR_ZWSP = 0x200b;
-
 // set text and set a number of parameters for creating a layout (width, tabstops, strategy)
 static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
         jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3796c9c..1cb0455 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1221,6 +1221,14 @@
         android:label="@string/permlab_cameraDisableTransmitLed"
         android:description="@string/permdesc_cameraDisableTransmitLed" />
 
+    <!-- Allows sending the camera service notifications about system-wide events.
+        @hide -->
+    <permission android:name="android.permission.CAMERA_SEND_SYSTEM_EVENTS"
+        android:permissionGroup="android.permission-group.CAMERA"
+        android:protectionLevel="signature|system"
+        android:label="@string/permdesc_cameraSendSystemEvent"
+        android:description="@string/permdesc_cameraSendSystemEvent" />
+
     <!-- =========================================== -->
     <!-- Permissions associated with telephony state -->
     <!-- =========================================== -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dcb4b9e..b6d32b2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7759,6 +7759,8 @@
             <enum name="multi-select" value="4" />
             <enum name="integer" value="5" />
             <enum name="string" value="6" />
+            <enum name="bundle" value="7" />
+            <enum name="bundle_array" value="8" />
         </attr>
         <attr name="title" />
         <attr name="description" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 702510a..0a77014 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1758,6 +1758,8 @@
     <string name="permlab_cameraDisableTransmitLed">disable transmit indicator LED when camera is in use</string>
     <!-- Description of a camera app permission, listed so the user can choose whether or not they want to allow it to disable the may-transmit light indicator. -->
     <string name="permdesc_cameraDisableTransmitLed">Allows a pre-installed system application to disable the camera use indicator LED.</string>
+    <!-- Description of a camera app permission, listed so that the user can send the camera service notifications about system-wide events. -->
+    <string name="permdesc_cameraSendSystemEvent">Allows a pre-installed system application to send the camera service system events.</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_brick" product="tablet">permanently disable tablet</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bfaea8f..b07d338 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -113,6 +113,9 @@
     <application android:theme="@style/Theme">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
+        <meta-data
+            android:name="android.content.APP_RESTRICTIONS"
+            android:resource="@xml/app_restrictions" />
         <activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index ce0d9a2..04b0478 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -135,4 +135,8 @@
     <string name="first">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.</string>
     <string name="actor">Abe Lincoln</string>
     <string name="caption">Lincoln adressing the crowd at Gettysburgh</string>
+
+    <!-- RestrictionsManagerTest -->
+    <string name="restrictionManager_title">Title</string>
+    <string name="restrictionManager_desc">Description</string>
 </resources>
diff --git a/core/tests/coretests/res/xml/app_restrictions.xml b/core/tests/coretests/res/xml/app_restrictions.xml
new file mode 100644
index 0000000..c84cabcc
--- /dev/null
+++ b/core/tests/coretests/res/xml/app_restrictions.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
+    <restriction android:key="hidden_key"
+                 android:restrictionType="hidden"/>
+    <restriction
+            android:defaultValue="true"
+            android:description="@string/restrictionManager_desc"
+            android:key="bool_key"
+            android:restrictionType="bool"
+            android:title="@string/restrictionManager_title"/>
+    <restriction
+            android:defaultValue="test"
+            android:key="string_key"
+            android:restrictionType="string"/>
+
+    <restriction android:key="int_key"
+                 android:restrictionType="integer"
+                 android:defaultValue="15"/>
+    <restriction android:key="bundle_key"
+                 android:restrictionType="bundle">
+        <restriction
+                android:key="bundle_string_key"
+                android:restrictionType="string"/>
+        <restriction
+                android:defaultValue="true"
+                android:key="bundle_bool_key"
+                android:restrictionType="bool"/>
+
+    </restriction>
+    <restriction android:key="bundle_array_key"
+                 android:restrictionType="bundle_array">
+        <restriction android:key="bundle_array_int"
+                     android:restrictionType="integer"/>
+        <restriction android:key="bundle_array_bundle_key"
+                     android:restrictionType="bundle">
+            <restriction android:key="bundle_array_bundle_int_key"
+                         android:restrictionType="integer"/>
+        </restriction>
+    </restriction>
+</restrictions>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
new file mode 100644
index 0000000..8921924
--- /dev/null
+++ b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.content;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class RestrictionsManagerTest extends AndroidTestCase {
+    private RestrictionsManager mRm;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mRm = (RestrictionsManager) mContext.getSystemService(Context.RESTRICTIONS_SERVICE);
+    }
+
+    public void testGetManifestRestrictions() {
+        String packageName = getContext().getPackageName();
+        List<RestrictionEntry> manifestRestrictions = mRm.getManifestRestrictions(packageName);
+        assertEquals(6, manifestRestrictions.size());
+        Set<String> verifiedKeys = new HashSet<>(Arrays.asList("bundle_key", "bundle_array_key",
+                "bundle_array_bundle_key"));
+        for (RestrictionEntry entry : manifestRestrictions) {
+            if ("bundle_key".equals(entry.getKey())) {
+                assertEquals("bundle_key entry should have 2 children entries",
+                        2, entry.getRestrictions().length);
+                verifiedKeys.remove(entry.getKey());
+            } else if ("bundle_array_key".equals(entry.getKey())) {
+                assertEquals("bundle_array_key should have 2 children entries",
+                        2, entry.getRestrictions().length);
+                assertNotNull(entry.getRestrictions());
+                for (RestrictionEntry childEntry : entry.getRestrictions()) {
+                    if ("bundle_array_bundle_key".equals(childEntry.getKey())) {
+                        assertNotNull(childEntry.getRestrictions());
+                        assertEquals("bundle_array_bundle_key should have 1 child entry",
+                                1, childEntry.getRestrictions().length);
+                        verifiedKeys.remove(childEntry.getKey());
+                    }
+                }
+                verifiedKeys.remove(entry.getKey());
+            }
+        }
+        assertTrue("Entries" + verifiedKeys + " were not found", verifiedKeys.isEmpty());
+    }
+
+    public void testConvertRestrictionsToBundle() {
+        String packageName = getContext().getPackageName();
+        List<RestrictionEntry> manifestRestrictions = mRm.getManifestRestrictions(packageName);
+        Bundle bundle = RestrictionsManager.convertRestrictionsToBundle(manifestRestrictions);
+        assertEquals(6, bundle.size());
+        Bundle childBundle = bundle.getBundle("bundle_key");
+        assertNotNull(childBundle);
+        assertEquals(2, childBundle.size());
+        Parcelable[] childBundleArray = bundle.getParcelableArray("bundle_array_key");
+        assertEquals(2, childBundleArray.length);
+    }
+
+}
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index ada8c12..c63c8ba 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -356,6 +356,108 @@
     public static final int RAW10 = 0x25;
 
     /**
+     * <p>
+     * Android 12-bit raw format
+     * </p>
+     * <p>
+     * This is a single-plane, 12-bit per pixel, densely packed (in each row),
+     * unprocessed format, usually representing raw Bayer-pattern images coming
+     * from an image sensor.
+     * </p>
+     * <p>
+     * In an image buffer with this format, starting from the first pixel of each
+     * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first
+     * and second byte contains the top 8 bits of first and second pixel. The third
+     * byte contains the 4 least significant bits of the two pixels, the exact layout
+     * data for each two consecutive pixels is illustrated below (Pi[j] stands for
+     * the jth bit of the ith pixel):
+     * </p>
+     * <table>
+     * <thead>
+     * <tr>
+     * <th align="center"></th>
+     * <th align="center">bit 7</th>
+     * <th align="center">bit 6</th>
+     * <th align="center">bit 5</th>
+     * <th align="center">bit 4</th>
+     * <th align="center">bit 3</th>
+     * <th align="center">bit 2</th>
+     * <th align="center">bit 1</th>
+     * <th align="center">bit 0</th>
+     * </tr>
+     * </thead> <tbody>
+     * <tr>
+     * <td align="center">Byte 0:</td>
+     * <td align="center">P0[11]</td>
+     * <td align="center">P0[10]</td>
+     * <td align="center">P0[ 9]</td>
+     * <td align="center">P0[ 8]</td>
+     * <td align="center">P0[ 7]</td>
+     * <td align="center">P0[ 6]</td>
+     * <td align="center">P0[ 5]</td>
+     * <td align="center">P0[ 4]</td>
+     * </tr>
+     * <tr>
+     * <td align="center">Byte 1:</td>
+     * <td align="center">P1[11]</td>
+     * <td align="center">P1[10]</td>
+     * <td align="center">P1[ 9]</td>
+     * <td align="center">P1[ 8]</td>
+     * <td align="center">P1[ 7]</td>
+     * <td align="center">P1[ 6]</td>
+     * <td align="center">P1[ 5]</td>
+     * <td align="center">P1[ 4]</td>
+     * </tr>
+     * <tr>
+     * <td align="center">Byte 2:</td>
+     * <td align="center">P1[ 3]</td>
+     * <td align="center">P1[ 2]</td>
+     * <td align="center">P1[ 1]</td>
+     * <td align="center">P1[ 0]</td>
+     * <td align="center">P0[ 3]</td>
+     * <td align="center">P0[ 2]</td>
+     * <td align="center">P0[ 1]</td>
+     * <td align="center">P0[ 0]</td>
+     * </tr>
+     * </tbody>
+     * </table>
+     * <p>
+     * This format assumes
+     * <ul>
+     * <li>a width multiple of 4 pixels</li>
+     * <li>an even height</li>
+     * </ul>
+     * </p>
+     *
+     * <pre>size = row stride * height</pre> where the row stride is in <em>bytes</em>,
+     * not pixels.
+     *
+     * <p>
+     * Since this is a densely packed format, the pixel stride is always 0. The
+     * application must use the pixel data layout defined in above table to
+     * access each row data. When row stride is equal to {@code width * (12 / 8)}, there
+     * will be no padding bytes at the end of each row, the entire image data is
+     * densely packed. When stride is larger than {@code width * (12 / 8)}, padding
+     * bytes will be present at the end of each row.
+     * </p>
+     * <p>
+     * For example, the {@link android.media.Image} object can provide data in
+     * this format from a {@link android.hardware.camera2.CameraDevice} (if
+     * supported) through a {@link android.media.ImageReader} object. The
+     * {@link android.media.Image#getPlanes() Image#getPlanes()} will return a
+     * single plane containing the pixel data. The pixel stride is always 0 in
+     * {@link android.media.Image.Plane#getPixelStride()}, and the
+     * {@link android.media.Image.Plane#getRowStride()} describes the vertical
+     * neighboring pixel distance (in bytes) between adjacent rows.
+     * </p>
+     *
+     * @see android.media.Image
+     * @see android.media.ImageReader
+     * @see android.hardware.camera2.CameraDevice
+     */
+    public static final int RAW12 = 0x26;
+
+    /**
      * Android dense depth image format.
      *
      * Each pixel is 16 bits, representing a depth ranging measurement from
@@ -445,6 +547,8 @@
                 return 16;
             case RAW10:
                 return 10;
+            case RAW12:
+                return 12;
         }
         return -1;
     }
@@ -472,6 +576,7 @@
             case YUV_420_888:
             case RAW_SENSOR:
             case RAW10:
+            case RAW12:
             case DEPTH16:
             case DEPTH_POINT_CLOUD:
             case PRIVATE:
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 1d16ca1..846d1f1 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -457,7 +457,7 @@
 
         String keyAlgorithmString = key.getAlgorithm();
         @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
-        @KeyStoreKeyConstraints.AlgorithmEnum Integer digest;
+        @KeyStoreKeyConstraints.DigestEnum Integer digest;
         try {
             keyAlgorithm =
                     KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString);
@@ -493,12 +493,6 @@
         if (digest != null) {
             args.addInt(KeymasterDefs.KM_TAG_DIGEST,
                     KeyStoreKeyConstraints.Digest.toKeymaster(digest));
-        }
-        if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
-            if (digest == null) {
-                throw new IllegalStateException("Digest algorithm must be specified for key"
-                        + " algorithm " + keyAlgorithmString);
-            }
             Integer digestOutputSizeBytes =
                     KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
             if (digestOutputSizeBytes != null) {
@@ -507,6 +501,12 @@
                 args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
             }
         }
+        if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (digest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + keyAlgorithmString);
+            }
+        }
 
         @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
                 ? params.getPurposes()
@@ -560,6 +560,12 @@
         // TODO: Remove this once keymaster does not require us to specify the size of imported key.
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
 
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+                || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
+            // Permit caller-specified IV. This is needed for the Cipher abstraction.
+            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+        }
+
         Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
         String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
         int errorCode = mKeyStore.importKey(
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 7313c48..6cf9b7a 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -41,7 +41,30 @@
         put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());
 
         // javax.crypto.Mac
-        put("Mac.HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());
-        put("Mac.HmacSHA256 SupportedKeyClasses", KeyStoreSecretKey.class.getName());
+        putMacImpl("HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());
+
+        // javax.crypto.Cipher
+        putSymmetricCipherImpl("AES/ECB/NoPadding",
+                KeyStoreCipherSpi.AES.ECB.NoPadding.class.getName());
+        putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
+                KeyStoreCipherSpi.AES.ECB.PKCS7Padding.class.getName());
+
+        putSymmetricCipherImpl("AES/CBC/NoPadding",
+                KeyStoreCipherSpi.AES.CBC.NoPadding.class.getName());
+        putSymmetricCipherImpl("AES/CBC/PKCS7Padding",
+                KeyStoreCipherSpi.AES.CBC.PKCS7Padding.class.getName());
+
+        putSymmetricCipherImpl("AES/CTR/NoPadding",
+                KeyStoreCipherSpi.AES.CTR.NoPadding.class.getName());
+    }
+
+    private void putMacImpl(String algorithm, String implClass) {
+        put("Mac." + algorithm, implClass);
+        put("Mac." + algorithm + " SupportedKeyClasses", KeyStoreSecretKey.class.getName());
+    }
+
+    private void putSymmetricCipherImpl(String transformation, String implClass) {
+        put("Cipher." + transformation, implClass);
+        put("Cipher." + transformation + " SupportedKeyClasses", KeyStoreSecretKey.class.getName());
     }
 }
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
new file mode 100644
index 0000000..6863236
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -0,0 +1,540 @@
+package android.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+public abstract class KeyStoreCipherSpi extends CipherSpi {
+
+    public abstract static class AES extends KeyStoreCipherSpi {
+        protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode,
+                @KeyStoreKeyConstraints.PaddingEnum int padding, boolean ivUsed) {
+            super(KeyStoreKeyConstraints.Algorithm.AES,
+                    blockMode,
+                    padding,
+                    16,
+                    ivUsed);
+        }
+
+        public abstract static class ECB extends AES {
+            protected ECB(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.ECB, padding, false);
+            }
+
+            public static class NoPadding extends ECB {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+
+            public static class PKCS7Padding extends ECB {
+                public PKCS7Padding() {
+                    super(KeyStoreKeyConstraints.Padding.PKCS7);
+                }
+            }
+        }
+
+        public abstract static class CBC extends AES {
+            protected CBC(@KeyStoreKeyConstraints.BlockModeEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.CBC, padding, true);
+            }
+
+            public static class NoPadding extends CBC {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+
+            public static class PKCS7Padding extends CBC {
+                public PKCS7Padding() {
+                    super(KeyStoreKeyConstraints.Padding.PKCS7);
+                }
+            }
+        }
+
+        public abstract static class CTR extends AES {
+            protected CTR(@KeyStoreKeyConstraints.BlockModeEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.CTR, padding, true);
+            }
+
+            public static class NoPadding extends CTR {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+        }
+    }
+
+    private final KeyStore mKeyStore;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPadding;
+    private final int mBlockSizeBytes;
+    private final boolean mIvUsed;
+
+    // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
+    // doFinal finishes.
+    protected boolean mEncrypting;
+    private KeyStoreSecretKey mKey;
+    private SecureRandom mRng;
+    private boolean mFirstOperationInitiated;
+    byte[] mIv;
+
+    // Fields below must be reset
+    private byte[] mAdditionalEntropyForBegin;
+    /**
+     * Token referencing this operation inside keystore service. It is initialized by
+     * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some
+     * error conditions in between.
+     */
+    private IBinder mOperationToken;
+    private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+
+    protected KeyStoreCipherSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockMode,
+            @KeyStoreKeyConstraints.PaddingEnum int padding,
+            int blockSizeBytes,
+            boolean ivUsed) {
+        mKeyStore = KeyStore.getInstance();
+        mAlgorithm = algorithm;
+        mBlockMode = blockMode;
+        mPadding = padding;
+        mBlockSizeBytes = blockSizeBytes;
+        mIvUsed = ivUsed;
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters();
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters(params);
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters(params);
+        ensureKeystoreOperationInitialized();
+    }
+
+    private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        reset();
+        if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        mKey = (KeyStoreSecretKey) key;
+        mRng = random;
+        mIv = null;
+        mFirstOperationInitiated = false;
+
+        if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.DECRYPT_MODE)) {
+            throw new UnsupportedOperationException(
+                    "Only ENCRYPT and DECRYPT modes supported. Mode: " + opmode);
+        }
+        mEncrypting = opmode == Cipher.ENCRYPT_MODE;
+    }
+
+    private void reset() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mMainDataStreamer = null;
+        mAdditionalEntropyForBegin = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() {
+        if (mMainDataStreamer != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        KeymasterArguments keymasterInputArgs = new KeymasterArguments();
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm);
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mBlockMode);
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mPadding);
+        addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
+
+        KeymasterArguments keymasterOutputArgs = new KeymasterArguments();
+        OperationResult opResult = mKeyStore.begin(
+                mKey.getAlias(),
+                mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
+                true, // permit aborting this operation if keystore runs out of resources
+                keymasterInputArgs,
+                mAdditionalEntropyForBegin,
+                keymasterOutputArgs);
+        mAdditionalEntropyForBegin = null;
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw new CryptoOperationException("Failed to start keystore operation",
+                    KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+        }
+
+        if (opResult.token == null) {
+            throw new CryptoOperationException("Keystore returned null operation token");
+        }
+        mOperationToken = opResult.token;
+        loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
+        mFirstOperationInitiated = true;
+        mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, opResult.token));
+    }
+
+    @Override
+    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+        ensureKeystoreOperationInitialized();
+
+        if (inputLen == 0) {
+            return null;
+        }
+
+        byte[] output;
+        try {
+            output = mMainDataStreamer.update(input, inputOffset, inputLen);
+        } catch (KeymasterException e) {
+            throw new CryptoOperationException("Keystore operation failed", e);
+        }
+
+        if (output.length == 0) {
+            return null;
+        }
+
+        return output;
+    }
+
+    @Override
+    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException {
+        ensureKeystoreOperationInitialized();
+
+        byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        ensureKeystoreOperationInitialized();
+
+        byte[] output;
+        try {
+            output = mMainDataStreamer.doFinal(input, inputOffset, inputLen);
+        } catch (KeymasterException e) {
+            switch (e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
+                    throw new IllegalBlockSizeException();
+                case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
+                    throw new BadPaddingException();
+                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+                    throw new AEADBadTagException();
+                default:
+                    throw new CryptoOperationException("Keystore operation failed", e);
+            }
+        }
+
+        reset();
+        return output;
+    }
+
+    @Override
+    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        return mBlockSizeBytes;
+    }
+
+    @Override
+    protected byte[] engineGetIV() {
+        return (mIv != null) ? mIv.clone() : null;
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        return inputLen + 3 * engineGetBlockSize();
+    }
+
+    @Override
+    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify block mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void engineSetPadding(String arg0) throws NoSuchPaddingException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify padding mode.
+        throw new UnsupportedOperationException();
+    }
+
+    // The methods below may need to be overridden by subclasses that use algorithm-specific
+    // parameters.
+
+    /**
+     * Returns algorithm-specific parameters used by this {@code CipherSpi} instance or {@code null}
+     * if no algorithm-specific parameters are used.
+     *
+     * <p>This implementation only handles the IV parameter.
+     */
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        if (!mIvUsed) {
+            return null;
+        }
+        if ((mIv != null) && (mIv.length > 0)) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+                params.init(new IvParameterSpec(mIv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new RuntimeException(
+                        "Failed to initialize AES AlgorithmParameters with an IV", e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameters.
+     *
+     * @param params algorithm parameters.
+     *
+     * @throws InvalidAlgorithmParameterException if some/all of the parameters cannot be
+     *         automatically configured and thus {@code Cipher.init} needs to be invoked with
+     *         explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvUsed) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException(
+                        "IvParameterSpec must be provided when decrypting");
+            }
+            return;
+        }
+        if (!(params instanceof IvParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
+        }
+        mIv = ((IvParameterSpec) params).getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameters.
+     *
+     * @param params algorithm parameters.
+     *
+     * @throws InvalidAlgorithmParameterException if some/all of the parameters cannot be
+     *         automatically configured and thus {@code Cipher.init} needs to be invoked with
+     *         explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvUsed) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+            return;
+        }
+
+        IvParameterSpec ivSpec;
+        try {
+            ivSpec = params.getParameterSpec(IvParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ", but not found in parameters: " + params, e);
+            }
+            mIv = null;
+            return;
+        }
+        mIv = ivSpec.getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameter.
+     *
+     * @throws InvalidKeyException if some/all of the parameters cannot be automatically configured
+     *         and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
+        if (!mIvUsed) {
+            return;
+        }
+
+        // IV is used
+        if (!mEncrypting) {
+            throw new InvalidKeyException("IV required when decrypting"
+                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+        }
+    }
+
+    /**
+     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+     *
+     * <p>The default implementation takes care of the IV.
+     *
+     * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+     *        parameters.
+     */
+    protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
+        if (!mFirstOperationInitiated) {
+            // First begin operation -- see if we need to provide additional entropy for IV
+            // generation.
+            if (mIvUsed) {
+                // IV is needed
+                if ((mIv == null) && (mEncrypting)) {
+                    // TODO: Switch to keymaster-generated IV code below once keymaster supports
+                    // that.
+                    // IV is needed but was not provided by the caller -- generate an IV.
+                    mIv = new byte[mBlockSizeBytes];
+                    SecureRandom rng = (mRng != null) ? mRng : new SecureRandom();
+                    rng.nextBytes(mIv);
+//                    // IV was not provided by the caller and thus will be generated by keymaster.
+//                    // Mix in some additional entropy from the provided SecureRandom.
+//                    if (mRng != null) {
+//                        mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
+//                        mRng.nextBytes(mAdditionalEntropyForBegin);
+//                    }
+                }
+            }
+        }
+
+        if ((mIvUsed) && (mIv != null)) {
+            keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to obtain algorithm-specific parameters from the result of the
+     * Keymaster's {@code begin} operation. Some of these parameters may need to be reused after
+     * {@code doFinal} by {@link #addAlgorithmSpecificParametersToBegin(KeymasterArguments)}.
+     *
+     * <p>The default implementation only takes care of the IV.
+     *
+     * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin}
+     *        operation.
+     */
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            KeymasterArguments keymasterArgs) {
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = keymasterArgs.getBlob(KeymasterDefs.KM_TAG_NONCE, null);
+        if ((returnedIv != null) && (returnedIv.length == 0)) {
+            returnedIv = null;
+        }
+
+        if (mIvUsed) {
+            if (mIv == null) {
+                mIv = returnedIv;
+            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+                throw new CryptoOperationException("IV in use differs from provided IV");
+            }
+        } else {
+            if (returnedIv != null) {
+                throw new CryptoOperationException(
+                        "IV in use despite IV not being used by this transformation");
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
index a37ddce..6385947 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -1,5 +1,6 @@
 package android.security;
 
+import android.os.IBinder;
 import android.security.keymaster.OperationResult;
 
 import java.io.ByteArrayOutputStream;
@@ -10,32 +11,36 @@
  * {@code update} and {@code finish} operations.
  *
  * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
- * update and finish operations. Firstly, KeyStore's update and finish operations can consume only a
- * limited amount of data in one go because the operations are marshalled via Binder. Secondly, the
- * update operation may consume less data than provided, in which case the caller has to buffer
- * the remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
  * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
  * various JCA crypto primitives.
  *
- * <p>KeyStore operation through which data is streamed is abstracted away as
- * {@link KeyStoreOperation} to avoid having this class deal with operation tokens and occasional
- * additional parameters to update and final operations.
+ * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
+ * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
+ * parameters to {@code update} and {@code final} operations.
  *
  * @hide
  */
 public class KeyStoreCryptoOperationChunkedStreamer {
-    public interface KeyStoreOperation {
+
+    /**
+     * Bidirectional chunked data stream over a KeyStore crypto operation.
+     */
+    public interface Stream {
         /**
-         * Returns the result of the KeyStore update operation or null if keystore couldn't be
-         * reached.
+         * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
+         * be reached.
          */
         OperationResult update(byte[] input);
 
         /**
-         * Returns the result of the KeyStore finish operation or null if keystore couldn't be
-         * reached.
+         * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
+         * be reached.
          */
-        OperationResult finish(byte[] input);
+        OperationResult finish();
     }
 
     // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
@@ -43,19 +48,19 @@
     private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
-    private final KeyStoreOperation mKeyStoreOperation;
+    private final Stream mKeyStoreStream;
     private final int mMaxChunkSize;
 
     private byte[] mBuffered = EMPTY_BYTE_ARRAY;
     private int mBufferedOffset;
     private int mBufferedLength;
 
-    public KeyStoreCryptoOperationChunkedStreamer(KeyStoreOperation operation) {
+    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
         this(operation, DEFAULT_MAX_CHUNK_SIZE);
     }
 
-    public KeyStoreCryptoOperationChunkedStreamer(KeyStoreOperation operation, int maxChunkSize) {
-        mKeyStoreOperation = operation;
+    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
+        mKeyStoreStream = operation;
         mMaxChunkSize = maxChunkSize;
     }
 
@@ -73,10 +78,9 @@
             if ((mBufferedLength + inputLength) > mMaxChunkSize) {
                 // Too much input for one chunk -- extract one max-sized chunk and feed it into the
                 // update operation.
-                chunk = new byte[mMaxChunkSize];
-                System.arraycopy(mBuffered, mBufferedOffset, chunk, 0, mBufferedLength);
-                inputBytesInChunk = chunk.length - mBufferedLength;
-                System.arraycopy(input, inputOffset, chunk, mBufferedLength, inputBytesInChunk);
+                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
+                chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
+                        input, inputOffset, inputBytesInChunk);
             } else {
                 // All of available input fits into one chunk.
                 if ((mBufferedLength == 0) && (inputOffset == 0)
@@ -87,17 +91,16 @@
                     inputBytesInChunk = input.length;
                 } else {
                     // Need to combine buffered data with input data into one array.
-                    chunk = new byte[mBufferedLength + inputLength];
                     inputBytesInChunk = inputLength;
-                    System.arraycopy(mBuffered, mBufferedOffset, chunk, 0, mBufferedLength);
-                    System.arraycopy(input, inputOffset, chunk, mBufferedLength, inputLength);
+                    chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
+                            input, inputOffset, inputBytesInChunk);
                 }
             }
             // Update input array references to reflect that some of its bytes are now in mBuffered.
             inputOffset += inputBytesInChunk;
             inputLength -= inputBytesInChunk;
 
-            OperationResult opResult = mKeyStoreOperation.update(chunk);
+            OperationResult opResult = mKeyStoreStream.update(chunk);
             if (opResult == null) {
                 throw new KeyStoreConnectException();
             } else if (opResult.resultCode != KeyStore.NO_ERROR) {
@@ -176,53 +179,115 @@
             inputOffset = 0;
         }
 
-        byte[] updateOutput = null;
-        if ((mBufferedLength + inputLength) > mMaxChunkSize) {
-            updateOutput = update(input, inputOffset, inputLength);
-            inputOffset += inputLength;
-            inputLength = 0;
-        }
-        // All of available input fits into one chunk.
+        // Flush all buffered input and provided input into keystore/keymaster.
+        byte[] output = update(input, inputOffset, inputLength);
+        output = concat(output, flush());
 
-        byte[] finalChunk;
-        if ((mBufferedLength == 0) && (inputOffset == 0)
-                && (inputLength == input.length)) {
-            // Nothing buffered and all of input array needs to be fed into the finish operation.
-            finalChunk = input;
-        } else {
-            // Need to combine buffered data with input data into one array.
-            finalChunk = new byte[mBufferedLength + inputLength];
-            System.arraycopy(mBuffered, mBufferedOffset, finalChunk, 0, mBufferedLength);
-            System.arraycopy(input, inputOffset, finalChunk, mBufferedLength, inputLength);
-        }
-        mBuffered = EMPTY_BYTE_ARRAY;
-        mBufferedLength = 0;
-        mBufferedOffset = 0;
-
-        OperationResult opResult = mKeyStoreOperation.finish(finalChunk);
+        OperationResult opResult = mKeyStoreStream.finish();
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
             throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
         }
 
-        if (opResult.inputConsumed != finalChunk.length) {
-            throw new CryptoOperationException("Unexpected number of bytes consumed by finish: "
-                    + opResult.inputConsumed + " instead of " + finalChunk.length);
+        return concat(output, opResult.output);
+    }
+
+    /**
+     * Passes all of buffered input into the the KeyStore operation (via the {@code update}
+     * operation) and returns output.
+     */
+    public byte[] flush() throws KeymasterException {
+        if (mBufferedLength <= 0) {
+            return EMPTY_BYTE_ARRAY;
         }
 
-        // Return the concatenation of the output of update and finish.
-        byte[] result;
-        byte[] finishOutput = opResult.output;
-        if ((updateOutput == null) || (updateOutput.length == 0)) {
-            result = finishOutput;
-        } else if ((finishOutput == null) || (finishOutput.length == 0)) {
-            result = updateOutput;
-        } else {
-            result = new byte[updateOutput.length + finishOutput.length];
-            System.arraycopy(updateOutput, 0, result, 0, updateOutput.length);
-            System.arraycopy(finishOutput, 0, result, updateOutput.length, finishOutput.length);
+        byte[] chunk = subarray(mBuffered, mBufferedOffset, mBufferedLength);
+        mBuffered = EMPTY_BYTE_ARRAY;
+        mBufferedLength = 0;
+        mBufferedOffset = 0;
+
+        OperationResult opResult = mKeyStoreStream.update(chunk);
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
         }
-        return (result != null) ? result : EMPTY_BYTE_ARRAY;
+
+        if (opResult.inputConsumed < chunk.length) {
+            throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+                    + chunk.length + ", consumed: " + opResult.inputConsumed);
+        } else if (opResult.inputConsumed > chunk.length) {
+            throw new CryptoOperationException("Keystore consumed more input than provided"
+                    + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
+        }
+
+        return (opResult.output != null) ? opResult.output : EMPTY_BYTE_ARRAY;
+    }
+
+    private static byte[] concat(byte[] arr1, byte[] arr2) {
+        if ((arr1 == null) || (arr1.length == 0)) {
+            return arr2;
+        } else if ((arr2 == null) || (arr2.length == 0)) {
+            return arr1;
+        } else {
+            byte[] result = new byte[arr1.length + arr2.length];
+            System.arraycopy(arr1, 0, result, 0, arr1.length);
+            System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
+            return result;
+        }
+    }
+
+    private static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
+            int len2) {
+        if (len1 == 0) {
+            return subarray(arr2, offset2, len2);
+        } else if (len2 == 0) {
+            return subarray(arr1, offset1, len1);
+        } else {
+            byte[] result = new byte[len1 + len2];
+            System.arraycopy(arr1, offset1, result, 0, len1);
+            System.arraycopy(arr2, offset2, result, len1, len2);
+            return result;
+        }
+    }
+
+    private static byte[] subarray(byte[] arr, int offset, int len) {
+        if (len == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        if ((offset == 0) && (len == arr.length)) {
+            return arr;
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(arr, offset, result, 0, len);
+        return result;
+    }
+
+    /**
+     * Main data stream via a KeyStore streaming operation.
+     *
+     * <p>For example, for an encryption operation, this is the stream through which plaintext is
+     * provided and ciphertext is obtained.
+     */
+    public static class MainDataStream implements Stream {
+
+        private final KeyStore mKeyStore;
+        private final IBinder mOperationToken;
+
+        public MainDataStream(KeyStore keyStore, IBinder operationToken) {
+            mKeyStore = keyStore;
+            mOperationToken = operationToken;
+        }
+
+        @Override
+        public OperationResult update(byte[] input) {
+            return mKeyStore.update(mOperationToken, null, input);
+        }
+
+        @Override
+        public OperationResult finish() {
+            return mKeyStore.finish(mOperationToken, null, null);
+        }
     }
 }
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index 3080d7b..e3c98b8 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -93,7 +93,8 @@
             throw new CryptoOperationException("Keystore returned null operation token");
         }
         mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
-                new KeyStoreStreamingConsumer(mKeyStore, mOperationToken));
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, mOperationToken));
     }
 
     @Override
@@ -147,28 +148,4 @@
             super.finalize();
         }
     }
-
-    /**
-     * KeyStore-backed consumer of {@code MacSpi}'s chunked stream.
-     */
-    private static class KeyStoreStreamingConsumer
-            implements KeyStoreCryptoOperationChunkedStreamer.KeyStoreOperation {
-        private final KeyStore mKeyStore;
-        private final IBinder mOperationToken;
-
-        private KeyStoreStreamingConsumer(KeyStore keyStore, IBinder operationToken) {
-            mKeyStore = keyStore;
-            mOperationToken = operationToken;
-        }
-
-        @Override
-        public OperationResult update(byte[] input) {
-            return mKeyStore.update(mOperationToken, null, input);
-        }
-
-        @Override
-        public OperationResult finish(byte[] input) {
-            return mKeyStore.finish(mOperationToken, null, input);
-        }
-    }
 }
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index b5e2436..58ea388 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -222,16 +222,6 @@
                     throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
             }
         }
-
-        /**
-         * @hide
-         */
-        public static String toJCAKeyPairAlgorithm(@AlgorithmEnum int algorithm) {
-            switch (algorithm) {
-                default:
-                    throw new IllegalArgumentException("Unsupported key alorithm: " + algorithm);
-            }
-        }
     }
 
     @Retention(RetentionPolicy.SOURCE)
@@ -306,6 +296,20 @@
                     throw new IllegalArgumentException("Unknown padding: " + padding);
             }
         }
+
+        /**
+         * @hide
+         */
+        public static @PaddingEnum int fromJCAPadding(String padding) {
+            String paddingLower = padding.toLowerCase(Locale.US);
+            if ("nopadding".equals(paddingLower)) {
+                return NONE;
+            } else if ("pkcs7padding".equals(paddingLower)) {
+                return PKCS7;
+            } else {
+                throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
     }
 
     @Retention(RetentionPolicy.SOURCE)
@@ -418,7 +422,7 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({BlockMode.ECB})
+    @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
     public @interface BlockModeEnum {}
 
     /**
@@ -427,11 +431,15 @@
     public static abstract class BlockMode {
         private BlockMode() {}
 
-        /**
-         * Electronic Codebook (ECB) block mode.
-         */
+        /** Electronic Codebook (ECB) block mode. */
         public static final int ECB = 0;
 
+        /** Cipher Block Chaining (CBC) block mode. */
+        public static final int CBC = 1;
+
+        /** Counter (CTR) block mode. */
+        public static final int CTR = 2;
+
         /**
          * @hide
          */
@@ -439,6 +447,10 @@
             switch (mode) {
                 case ECB:
                     return KeymasterDefs.KM_MODE_ECB;
+                case CBC:
+                    return KeymasterDefs.KM_MODE_CBC;
+                case CTR:
+                    return KeymasterDefs.KM_MODE_CTR;
                 default:
                     throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
@@ -451,6 +463,10 @@
             switch (mode) {
                 case KeymasterDefs.KM_MODE_ECB:
                     return ECB;
+                case KeymasterDefs.KM_MODE_CBC:
+                    return CBC;
+                case KeymasterDefs.KM_MODE_CTR:
+                    return CTR;
                 default:
                     throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
@@ -463,9 +479,29 @@
             switch (mode) {
                 case ECB:
                     return "ECB";
+                case CBC:
+                    return "CBC";
+                case CTR:
+                    return "CTR";
                 default:
                     throw new IllegalArgumentException("Unknown block mode: " + mode);
             }
         }
+
+        /**
+         * @hide
+         */
+        public static @BlockModeEnum int fromJCAMode(String mode) {
+            String modeLower = mode.toLowerCase(Locale.US);
+            if ("ecb".equals(modeLower)) {
+                return ECB;
+            } else if ("cbc".equals(modeLower)) {
+                return CBC;
+            } else if ("ctr".equals(modeLower)) {
+                return CTR;
+            } else {
+                throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
     }
 }
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index f1f9436..3e5b5c0 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -35,7 +35,7 @@
 
     private final KeyStore mKeyStore = KeyStore.getInstance();
     private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
-    private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mDigest;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
     private final int mDefaultKeySizeBits;
 
     private KeyGeneratorSpec mSpec;
@@ -76,12 +76,6 @@
         if (mDigest != null) {
             args.addInt(KeymasterDefs.KM_TAG_DIGEST,
                     KeyStoreKeyConstraints.Digest.toKeymaster(mDigest));
-        }
-        if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
-            if (mDigest == null) {
-                throw new IllegalStateException("Digest algorithm must be specified for key"
-                        + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
-            }
             Integer digestOutputSizeBytes =
                     KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest);
             if (digestOutputSizeBytes != null) {
@@ -90,6 +84,12 @@
                 args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
             }
         }
+        if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (mDigest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
+            }
+        }
         int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
         @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 48f5dc1..65be9e1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -138,7 +138,7 @@
     int regionSize = ashmem_get_size_region(ashmemfd);
     if (regionSize < static_cast<int>(sizeof(ProfileData))) {
         ALOGW("Ashmem region is too small! Received %d, required %u",
-                regionSize, sizeof(ProfileData));
+                regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
         return;
     }
     ProfileData* newData = reinterpret_cast<ProfileData*>(
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 9fc7e8e..708c083 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -431,6 +431,19 @@
             pData = buffer->data;
             dataSize = buffer->stride * buffer->height;
             break;
+        case HAL_PIXEL_FORMAT_RAW12:
+            // Single plane 10bpp bayer data.
+            ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+            LOG_ALWAYS_FATAL_IF(buffer->width % 4,
+                                "Width is not multiple of 4 %d", buffer->width);
+            LOG_ALWAYS_FATAL_IF(buffer->height % 2,
+                                "Height is not even %d", buffer->height);
+            LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8),
+                                "stride (%d) should be at least %d",
+                                buffer->stride, buffer->width * 12 / 8);
+            pData = buffer->data;
+            dataSize = buffer->stride * buffer->height;
+            break;
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
             // Single plane, 32bpp.
@@ -492,8 +505,10 @@
             break;
         case HAL_PIXEL_FORMAT_BLOB:
         case HAL_PIXEL_FORMAT_RAW10:
-            // Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
-            // single plane, row and pixel strides are 0.
+        case HAL_PIXEL_FORMAT_RAW12:
+            // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data,
+            // those are single plane data with pixel stride 0 since they don't really have a
+            // well defined pixel stride
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             pixelStride = 0;
             break;
@@ -549,12 +564,14 @@
             rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
             break;
         case HAL_PIXEL_FORMAT_BLOB:
-            // Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
-            // single plane, row and pixel strides are 0.
+            // Blob is used for JPEG data. It is single plane and has 0 row stride and
+            // 0 pixel stride
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             rowStride = 0;
             break;
         case HAL_PIXEL_FORMAT_RAW10:
+        case HAL_PIXEL_FORMAT_RAW12:
+            // RAW10 and RAW12 are used for 10-bit and 12-bit raw data, they are single plane
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             rowStride = buffer->stride;
             break;
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index c4daaa9..2b2e611 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -273,8 +273,8 @@
     }
 
     /**
-     * @hide
      * Enable/Disable AutoPadding for Vec3 elements.
+     * By default: Diabled.
      *
      * @param useAutoPadding True: enable AutoPadding; flase: disable AutoPadding
      *
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index 4a56572..a5da519 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -24,25 +24,39 @@
 import java.util.Map;
 
 /**
+ * ScriptGroup2 is a new, enhanced API for script groups.
+ * A script group is a collection of kernels or invocable functions, with
+ * data dependencies defined among them. A script group is launched for
+ * execution as a whole, rather than launching each kernel or invocable function
+ * separately. Once created, a script group can be repeatedly used with
+ * different inputs.
+ * <p>
+ * In the new ScriptGroup2 API, a script group is modeled using closures.
+ * A closure, in this context, is defined as a function call to a kernel or
+ * invocable function. Each function argument or global variable accessed inside
+ * the function is bound to 1) a known value, 2) a script group input, or 3) a
+ * future. A future is the output of a closure, i.e., the return value of the
+ * function or a global variable written by that function.
+ * <p>
+ * A script group is a directed acyclic graph (DAG), in which closures are the
+ * vertices and the dependencies among them are the edges.
+ * The way the ScriptGroup2 API is designed makes cycles impossible in a script
+ * group. For example, it is impossible to make forward references to futures,
+ * i.e., it is impossible to set as input to a closure the future from itself or
+ * a future from another closure that directly or indirectly depends on it.
+ * <p>
+ * Grouping kernels and invocable functions together allows to execute them more
+ * efficiently. Runtime and compiler optimizations are applied to script
+ * groups, to reduce computation or communication overhead, and to make more
+ * efficient use of the CPU and the GPU.
+ */
 
-******************************
-You have tried to change the API from what has been previously approved.
-
-To make these errors go away, you have two choices:
-1) You can add "@hide" javadoc comments to the methods, etc. listed in the
-errors above.
-
-2) You can update current.txt by executing the following command:
-make update-api
-
-To submit the revised current.txt to the main Android repository,
-you will need approval.
-******************************
-
-@hide Pending Android public API approval.
-*/
 public class ScriptGroup2 extends BaseObj {
 
+    /**
+     * An opaque class for closures
+     */
+
     public static class Closure extends BaseObj {
         private Allocation mReturnValue;
         private Map<Script.FieldID, Object> mBindings;
@@ -132,7 +146,6 @@
                     UnboundValue unbound = (UnboundValue)obj;
                     unbound.addReference(this, fieldID);
                 } else {
-                    // TODO(yangni): Verify obj not a future.
                     retrieveValueAndDependenceInfo(rs, i, obj, values,
                                                    sizes, depClosures, depFieldIDs);
                 }
@@ -174,6 +187,12 @@
             sizes[index] = vs.size;
         }
 
+        /**
+         * Returns the future for the return value
+         *
+         * @return a future
+         */
+
         public Future getReturn() {
             if (mReturnFuture == null) {
                 mReturnFuture = new Future(this, null, mReturnValue);
@@ -182,6 +201,13 @@
             return mReturnFuture;
         }
 
+        /**
+         * Returns the future for a global variable
+         *
+         * @param field the field ID for the global variable
+         * @return a future
+         */
+
         public Future getGlobal(Script.FieldID field) {
             Future f = mGlobalFuture.get(field);
 
@@ -234,6 +260,10 @@
         }
     }
 
+    /**
+     * An opaque class for futures
+     */
+
     public static class Future {
         Closure mClosure;
         Script.FieldID mFieldID;
@@ -250,6 +280,10 @@
         Object getValue() { return mValue; }
     }
 
+    /**
+     * An opaque class for unbound values (a.k.a. script group inputs)
+     */
+
     public static class UnboundValue {
         // Either mFieldID or mArgIndex should be set but not both.
         List<Pair<Closure, Script.FieldID>> mFieldID;
@@ -309,6 +343,13 @@
         setID(id);
     }
 
+    /**
+     * Executes a script group
+     *
+     * @param inputs inputs to the script group
+     * @return outputs of the script group as an array of objects
+     */
+
     public Object[] execute(Object... inputs) {
         if (inputs.length < mInputs.size()) {
             Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
@@ -343,32 +384,95 @@
     }
 
     /**
-       @hide Pending Android public API approval.
-    */
+     * A class representing a binding of a value to a global variable in a
+     * kernel or invocable function. Such a binding can be used to create a
+     * closure.
+     */
+
     public static final class Binding {
-        public Script.FieldID mField;
-        public Object mValue;
+        private Script.FieldID mField;
+        private Object mValue;
+
+        /**
+         * Returns a Binding object that binds value to field
+         *
+         * @param field the Script.FieldID of the global variable
+         * @param value the value
+         */
+
         public Binding(Script.FieldID field, Object value) {
             mField = field;
             mValue = value;
         }
+
+        /**
+         * Returns the field ID
+         */
+
+        public Script.FieldID getField() { return mField; }
+
+        /**
+         * Returns the value
+         */
+
+        public Object getValue() { return mValue; }
     }
 
     /**
-       @hide Pending Android public API approval.
-    */
+     * The builder class to create a script group.
+     * <p>
+     * Closures are created using the {@link #addKernel} or {@link #addInvoke}
+     * methods.
+     * When a closure is created, futures from previously created closures
+     * can be used as inputs.
+     * Unbound values can be used as inputs to create closures as well.
+     * An unbound value is created using the {@link #addInput} method.
+     * Unbound values become inputs to the script group to be created,
+     * in the order that they are added.
+     * A script group is created by a call to the {@link #create} method, which
+     * accepts an array of futures as the outputs for the script group.
+     * <p>
+     * Closures in a script group can be evaluated in any order as long as the
+     * following conditions are met.
+     * First, a closure must be evaluated before any other closures that take its
+     * futures as inputs.
+     * Second, all closures added before an invoke closure must be evaluated
+     * before it.
+     * Third, all closures added after an invoke closure must be evaluated after
+     * it.
+     * <p>
+     * As a special case, the order that the closures are added is a legal
+     * evaluation order. However, other evaluation orders are allowed, including
+     * concurrently evaluating independent closures.
+     */
+
     public static final class Builder {
         RenderScript mRS;
         List<Closure> mClosures;
         List<UnboundValue> mInputs;
         private static final String TAG = "ScriptGroup2.Builder";
 
+        /**
+         * Returns a Builder object
+         *
+         * @param rs the RenderScript context
+         */
         public Builder(RenderScript rs) {
             mRS = rs;
             mClosures = new ArrayList<Closure>();
             mInputs = new ArrayList<UnboundValue>();
         }
 
+        /**
+         * Adds a closure for a kernel
+         *
+         * @param k Kernel ID for the kernel function
+         * @param returnType Allocation type for the return value
+         * @param args arguments to the kernel function
+         * @param globalBindings bindings for global variables
+         * @return a closure
+         */
+
         public Closure addKernel(Script.KernelID k, Type returnType, Object[] args,
                                  Map<Script.FieldID, Object> globalBindings) {
             Closure c = new Closure(mRS, k, returnType, args, globalBindings);
@@ -376,6 +480,15 @@
             return c;
         }
 
+        /**
+         * Adds a closure for an invocable function
+         *
+         * @param invoke Invoke ID for the invocable function
+         * @param args arguments to the invocable function
+         * @param globalBindings bindings for global variables
+         * @return a closure
+         */
+
         public Closure addInvoke(Script.InvokeID invoke, Object[] args,
                                  Map<Script.FieldID, Object> globalBindings) {
             Closure c = new Closure(mRS, invoke, args, globalBindings);
@@ -383,12 +496,25 @@
             return c;
         }
 
+        /**
+         * Adds a script group input
+         *
+         * @return a unbound value that can be used to create a closure
+         */
         public UnboundValue addInput() {
             UnboundValue unbound = new UnboundValue();
             mInputs.add(unbound);
             return unbound;
         }
 
+        /**
+         * Adds a closure for a kernel
+         *
+         * @param k Kernel ID for the kernel function
+         * @param argsAndBindings arguments followed by bindings for global variables
+         * @return a closure
+         */
+
         public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
             ArrayList<Object> args = new ArrayList<Object>();
             Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
@@ -398,6 +524,14 @@
             return addKernel(k, returnType, args.toArray(), bindingMap);
         }
 
+        /**
+         * Adds a closure for an invocable function
+         *
+         * @param invoke Invoke ID for the invocable function
+         * @param argsAndBindings arguments followed by bindings for global variables
+         * @return a closure
+         */
+
         public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
             ArrayList<Object> args = new ArrayList<Object>();
             Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
@@ -407,6 +541,13 @@
             return addInvoke(invoke, args.toArray(), bindingMap);
         }
 
+        /**
+         * Creates a script group
+         *
+         * @param outputs futures intended as outputs of the script group
+         * @return a script group
+         */
+
         public ScriptGroup2 create(Future... outputs) {
             ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
             return ret;
@@ -428,7 +569,7 @@
                     return false;
                 }
                 Binding b = (Binding)argsAndBindings[i];
-                bindingMap.put(b.mField, b.mValue);
+                bindingMap.put(b.getField(), b.getValue());
             }
 
             return true;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 45b0fb2..5cc59e5 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3938,16 +3938,6 @@
                     return;
                 }
 
-                // Don't proceed unless we have already established package metadata
-                // for the current dataset via a key/value backup pass.
-                File stateDir = new File(mBaseStateDir, transport.transportDirName());
-                File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
-                if (pmState.length() <= 0) {
-                    Slog.i(TAG, "Full backup requested but dataset not yet initialized "
-                            + "via k/v backup pass; ignoring");
-                    return;
-                }
-
                 // Set up to send data to the transport
                 final int N = mPackages.size();
                 for (int i = 0; i < N; i++) {
@@ -4296,6 +4286,31 @@
         writeFullBackupScheduleAsync();
     }
 
+    private boolean fullBackupAllowable(IBackupTransport transport) {
+        if (transport == null) {
+            Slog.w(TAG, "Transport not present; full data backup not performed");
+            return false;
+        }
+
+        // Don't proceed unless we have already established package metadata
+        // for the current dataset via a key/value backup pass.
+        try {
+            File stateDir = new File(mBaseStateDir, transport.transportDirName());
+            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+            if (pmState.length() <= 0) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+                }
+                return false;
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to contact transport");
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Conditions are right for a full backup operation, so run one.  The model we use is
      * to perform one app backup per scheduled job execution, and to reschedule the job
@@ -4307,6 +4322,7 @@
     boolean beginFullBackup(FullBackupJob scheduledJob) {
         long now = System.currentTimeMillis();
         FullBackupEntry entry = null;
+        long latency = MIN_FULL_BACKUP_INTERVAL;
 
         if (!mEnabled || !mProvisioned) {
             // Backups are globally disabled, so don't proceed.  We also don't reschedule
@@ -4338,17 +4354,41 @@
                 return false;
             }
 
-            entry = mFullBackupQueue.get(0);
-            long timeSinceRun = now - entry.lastBackup;
-            if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) {
-                // It's too early to back up the next thing in the queue, so bow out
+            // At this point we know that we have work to do, just not right now.  Any
+            // exit without actually running backups will also require that we
+            // reschedule the job.
+            boolean runBackup = true;
+
+            if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
                 if (MORE_DEBUG) {
-                    Slog.i(TAG, "Device ready but too early to back up next app");
+                    Slog.i(TAG, "Preconditions not met; not running full backup");
                 }
-                final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                runBackup = false;
+                // Typically this means we haven't run a key/value backup yet.  Back off
+                // full-backup operations by the key/value job's run interval so that
+                // next time we run, we are likely to be able to make progress.
+                latency = KeyValueBackupJob.BATCH_INTERVAL;
+            }
+
+            if (runBackup) {
+                entry = mFullBackupQueue.get(0);
+                long timeSinceRun = now - entry.lastBackup;
+                runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+                if (!runBackup) {
+                    // It's too early to back up the next thing in the queue, so bow out
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Device ready but too early to back up next app");
+                    }
+                    // Wait until the next app in the queue falls due for a full data backup
+                    latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                }
+            }
+
+            if (!runBackup) {
+                final long deferTime = latency;     // pin for the closure
                 mBackupHandler.post(new Runnable() {
                     @Override public void run() {
-                        FullBackupJob.schedule(mContext, latency);
+                        FullBackupJob.schedule(mContext, deferTime);
                     }
                 });
                 return false;
@@ -8489,27 +8529,31 @@
             throw new IllegalStateException("Restore supported only for the device owner");
         }
 
-        if (DEBUG) {
-            Slog.d(TAG, "fullTransportBackup()");
-        }
-
-        CountDownLatch latch = new CountDownLatch(1);
-        PerformFullTransportBackupTask task =
-                new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
-        (new Thread(task, "full-transport-master")).start();
-        do {
-            try {
-                latch.await();
-                break;
-            } catch (InterruptedException e) {
-                // Just go back to waiting for the latch to indicate completion
+        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
+            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+        } else {
+            if (DEBUG) {
+                Slog.d(TAG, "fullTransportBackup()");
             }
-        } while (true);
 
-        // We just ran a backup on these packages, so kick them to the end of the queue
-        final long now = System.currentTimeMillis();
-        for (String pkg : pkgNames) {
-            enqueueFullBackup(pkg, now);
+            CountDownLatch latch = new CountDownLatch(1);
+            PerformFullTransportBackupTask task =
+                    new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
+            (new Thread(task, "full-transport-master")).start();
+            do {
+                try {
+                    latch.await();
+                    break;
+                } catch (InterruptedException e) {
+                    // Just go back to waiting for the latch to indicate completion
+                }
+            } while (true);
+
+            // We just ran a backup on these packages, so kick them to the end of the queue
+            final long now = System.currentTimeMillis();
+            for (String pkg : pkgNames) {
+                enqueueFullBackup(pkg, now);
+            }
         }
 
         if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index dc1c9d5..a4489c1 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -41,7 +41,7 @@
     // Once someone asks for a backup, this is how long we hold off, batching
     // up additional requests, before running the actual backup pass.  Privileged
     // callers can always trigger an immediate pass via BackupManager.backupNow().
-    private static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
+    static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
 
     // Random variation in next-backup scheduling time to avoid server load spikes
     private static final int FUZZ_MILLIS = 10 * 60 * 1000;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a64e6c3..1b32f57 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -109,7 +109,7 @@
 
     private static final int TIMEOUT_DELAY_MS = 1000 * 60;
     private static final String DATABASE_NAME = "accounts.db";
-    private static final int DATABASE_VERSION = 6;
+    private static final int DATABASE_VERSION = 7;
 
     private final Context mContext;
 
@@ -131,6 +131,8 @@
     private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
     private static final String ACCOUNTS_PASSWORD = "password";
     private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
+    private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
+            "last_password_entry_time_millis_epoch";
 
     private static final String TABLE_AUTHTOKENS = "authtokens";
     private static final String AUTHTOKENS_ID = "_id";
@@ -697,7 +699,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(fromAccounts, response, account.type, false,
-                    false /* stripAuthTokenFromResult */) {
+                    false /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
@@ -725,12 +728,43 @@
         }
     }
 
+    @Override
+    public boolean accountAuthenticated(final Account account) {
+        if (account == null) {
+            throw new IllegalArgumentException("account is null");
+        }
+        checkAuthenticateAccountsPermission(account);
+
+        final UserAccounts accounts = getUserAccountsForCaller();
+        int userId = Binder.getCallingUserHandle().getIdentifier();
+        if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
+            return false;
+        }
+        synchronized (accounts.cacheLock) {
+            final ContentValues values = new ContentValues();
+            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            int i = db.update(
+                    TABLE_ACCOUNTS,
+                    values,
+                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                    new String[] {
+                            account.name, account.type
+                    });
+            if (i > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void completeCloningAccount(IAccountManagerResponse response,
             final Bundle accountCredentials, final Account account, final UserAccounts targetUser) {
         long id = clearCallingIdentity();
         try {
             new Session(targetUser, response, account.type, false,
-                    false /* stripAuthTokenFromResult */) {
+                    false /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
@@ -795,6 +829,7 @@
                 values.put(ACCOUNTS_NAME, account.name);
                 values.put(ACCOUNTS_TYPE, account.type);
                 values.put(ACCOUNTS_PASSWORD, password);
+                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
                 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
                 if (accountId < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
@@ -885,7 +920,8 @@
         public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
                 Account account, String[] features) {
             super(accounts, response, account.type, false /* expectActivityLaunch */,
-                    true /* stripAuthTokenFromResult */);
+                    true /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */);
             mFeatures = features;
             mAccount = account;
         }
@@ -1184,7 +1220,8 @@
         public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
                 Account account, boolean expectActivityLaunch) {
             super(accounts, response, account.type, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */);
+                    true /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */);
             mAccount = account;
         }
 
@@ -1419,6 +1456,13 @@
             try {
                 final ContentValues values = new ContentValues();
                 values.put(ACCOUNTS_PASSWORD, password);
+                long time = 0;
+                // Only set current time, if it is a valid password. For clear password case, it
+                // should not be set.
+                if (password != null) {
+                    time = System.currentTimeMillis();
+                }
+                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, time);
                 final long accountId = getAccountIdLocked(db, account);
                 if (accountId >= 0) {
                     final String[] argsAccountId = {String.valueOf(accountId)};
@@ -1547,8 +1591,9 @@
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
         long identityToken = clearCallingIdentity();
         try {
-            new Session(accounts, response, accountType, false,
-                    false /* stripAuthTokenFromResult */) {
+            new Session(accounts, response, accountType, false /* expectActivityLaunch */,
+                    false /* stripAuthTokenFromResult */,  null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAuthTokenLabel"
@@ -1648,7 +1693,8 @@
             }
 
             new Session(accounts, response, account.type, expectActivityLaunch,
-                    false /* stripAuthTokenFromResult */) {
+                    false /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     if (loginOptions != null) loginOptions.keySet();
@@ -1842,7 +1888,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1917,7 +1964,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1973,7 +2021,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, account.name,
+                    true /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.confirmCredentials(this, account, options);
@@ -2009,7 +2058,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -2045,7 +2095,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.editProperties(this, mAccountType);
@@ -2071,7 +2122,8 @@
         public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
                 IAccountManagerResponse response, String type, String[] features, int callingUid) {
             super(accounts, response, type, false /* expectActivityLaunch */,
-                    true /* stripAuthTokenFromResult */);
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */);
             mCallingUid = callingUid;
             mFeatures = features;
         }
@@ -2437,6 +2489,9 @@
         final String mAccountType;
         final boolean mExpectActivityLaunch;
         final long mCreationTime;
+        final String mAccountName;
+        // Indicates if we need to add auth details(like last credential time)
+        final boolean mAuthDetailsRequired;
 
         public int mNumResults = 0;
         private int mNumRequestContinued = 0;
@@ -2448,7 +2503,8 @@
         protected final UserAccounts mAccounts;
 
         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
-                boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
+                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
+                boolean authDetailsRequired) {
             super();
             //if (response == null) throw new IllegalArgumentException("response is null");
             if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -2458,6 +2514,9 @@
             mAccountType = accountType;
             mExpectActivityLaunch = expectActivityLaunch;
             mCreationTime = SystemClock.elapsedRealtime();
+            mAccountName = accountName;
+            mAuthDetailsRequired = authDetailsRequired;
+
             synchronized (mSessions) {
                 mSessions.put(toString(), this);
             }
@@ -2592,6 +2651,16 @@
         public void onResult(Bundle result) {
             mNumResults++;
             Intent intent = null;
+            if (result != null && mAuthDetailsRequired) {
+                long lastAuthenticatedTime = DatabaseUtils.longForQuery(
+                        mAccounts.openHelper.getReadableDatabase(),
+                        "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from " +
+                                TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+                                + ACCOUNTS_TYPE + "=?",
+                        new String[]{mAccountName, mAccountType});
+                result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+                        lastAuthenticatedTime);
+            }
             if (result != null
                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
                 /*
@@ -2798,6 +2867,7 @@
                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
                     + ACCOUNTS_PASSWORD + " TEXT, "
                     + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
 
             db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
@@ -2833,6 +2903,11 @@
                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
         }
 
+        private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
+            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
+                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
+        }
+
         private void addOldAccountNameColumn(SQLiteDatabase db) {
             db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
         }
@@ -2892,6 +2967,11 @@
                 oldVersion++;
             }
 
+            if (oldVersion == 6) {
+                addLastSuccessfullAuthenticatedTimeColumn(db);
+                oldVersion++;
+            }
+
             if (oldVersion != newVersion) {
                 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c345d34..6543bc2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17494,8 +17494,12 @@
         mFullPssPending = true;
         mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
         mPendingPssProcesses.clear();
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord app = mLruProcesses.get(i);
+            if (app.thread == null
+                    || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                continue;
+            }
             if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
                 app.pssProcState = app.setProcState;
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
@@ -17811,8 +17815,8 @@
                 }
             }
         }
-        if (app.setProcState < 0 || ProcessList.procStatesDifferForMem(app.curProcState,
-                app.setProcState)) {
+        if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
             if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
                 // Experimental code to more aggressively collect pss while
                 // running test...  the problem is that this tends to collect
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7cf3b51..7c921ac 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -89,10 +90,10 @@
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
-    int curProcState = -1;      // Currently computed process state: ActivityManager.PROCESS_STATE_*
-    int repProcState = -1;      // Last reported process state
-    int setProcState = -1;      // Last set process state in process tracker
-    int pssProcState = -1;      // The proc state we are currently requesting pss for
+    int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
+    int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
+    int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean setIsForeground;    // Running foreground UI when last set?
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d8d361b..3b34541 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -346,8 +348,8 @@
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
-        } else if ((intentFlags & (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
-                | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT) {
+        } else if ((intentFlags & (FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS))
+                == FLAG_ACTIVITY_NEW_DOCUMENT) {
             // If the caller has not asked for the document to be retained, then we may
             // want to turn on auto-remove, depending on whether the target has set its
             // own document launch mode.
@@ -879,7 +881,8 @@
         for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
             if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
-                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
+                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
                             activityNdx > 0) {
                 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
                 break;
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
new file mode 100644
index 0000000..f9b17ed
--- /dev/null
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.android.server.camera;
+
+import android.content.Context;
+import android.hardware.ICameraService;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.server.SystemService;
+
+/**
+ * CameraService is the system_server analog to the camera service running in mediaserver.
+ *
+ * @hide
+ */
+public class CameraService extends SystemService {
+
+    /**
+     * This must match the ICameraService.aidl definition
+     */
+    private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+
+    // Event arguments to use with the camera service notifySystemEvent call:
+    public static final int NO_EVENT = 0; // NOOP
+    public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle
+
+    public CameraService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {}
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        super.onSwitchUser(userHandle);
+
+        /**
+         * Forward the user switch event to the native camera service running in mediaserver.
+         */
+        IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+        if (cameraServiceBinder == null) {
+            return; // Camera service not active, there is no need to evict user clients.
+        }
+        ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+        try {
+            cameraServiceRaw.notifySystemEvent(USER_SWITCHED, userHandle);
+        } catch (RemoteException e) {
+            // Do nothing, if camera service is dead, there is no need to evict user clients.
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index a415a84..15b68c7 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -571,7 +571,7 @@
     }
 
     private static boolean isAccessibleToUser(int uid, int callerUid) {
-        return callerUid == android.os.Process.SYSTEM_UID ||
+        return UserHandle.getAppId(callerUid) == android.os.Process.SYSTEM_UID ||
                 uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING
                 || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
     }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 20120de..daa6d64 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -204,7 +204,7 @@
      */
     public void copyFrom(PackageSettingBase base) {
         setPermissionsUpdatedForUserIds(base.getPermissionsUpdatedForUserIds());
-        getPermissionsState().copyFrom(base.getPermissionsState());
+        mPermissionsState.copyFrom(base.mPermissionsState);
         primaryCpuAbiString = base.primaryCpuAbiString;
         secondaryCpuAbiString = base.secondaryCpuAbiString;
         cpuAbiOverrideString = base.cpuAbiOverrideString;
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 3a7b6ee..0c7f79d 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -24,7 +24,7 @@
     int pkgFlags;
     int pkgPrivateFlags;
 
-    private final PermissionsState mPermissionsState;
+    protected final PermissionsState mPermissionsState;
     private int[] mPermissionsUpdatedForUserIds = PermissionsState.USERS_NONE;
 
     SettingBase(int pkgFlags, int pkgPrivateFlags) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8cc9d19..5e58cd9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -36,6 +36,7 @@
 import android.os.IUserManager;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -49,9 +50,11 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -71,6 +74,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import libcore.io.IoUtils;
+
 public class UserManagerService extends IUserManager.Stub {
 
     private static final String LOG_TAG = "UserManagerService";
@@ -107,6 +112,8 @@
     private static final String ATTR_TYPE_STRING = "s";
     private static final String ATTR_TYPE_BOOLEAN = "b";
     private static final String ATTR_TYPE_INTEGER = "i";
+    private static final String ATTR_TYPE_BUNDLE = "B";
+    private static final String ATTR_TYPE_BUNDLE_ARRAY = "BA";
 
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -1672,124 +1679,168 @@
 
     private Bundle readApplicationRestrictionsLocked(String packageName,
             int userId) {
+        AtomicFile restrictionsFile =
+                new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
+                        packageToRestrictionsFileName(packageName)));
+        return readApplicationRestrictionsLocked(restrictionsFile);
+    }
+
+    @VisibleForTesting
+    static Bundle readApplicationRestrictionsLocked(AtomicFile restrictionsFile) {
         final Bundle restrictions = new Bundle();
-        final ArrayList<String> values = new ArrayList<String>();
+        final ArrayList<String> values = new ArrayList<>();
 
         FileInputStream fis = null;
         try {
-            AtomicFile restrictionsFile =
-                    new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
-                            packageToRestrictionsFileName(packageName)));
             fis = restrictionsFile.openRead();
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(fis, null);
-            int type;
-            while ((type = parser.next()) != XmlPullParser.START_TAG
-                    && type != XmlPullParser.END_DOCUMENT) {
-                ;
-            }
-
-            if (type != XmlPullParser.START_TAG) {
+            XmlUtils.nextElement(parser);
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
                 Slog.e(LOG_TAG, "Unable to read restrictions file "
                         + restrictionsFile.getBaseFile());
                 return restrictions;
             }
-
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
-                    String key = parser.getAttributeValue(null, ATTR_KEY);
-                    String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
-                    String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
-                    if (multiple != null) {
-                        values.clear();
-                        int count = Integer.parseInt(multiple);
-                        while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                            if (type == XmlPullParser.START_TAG
-                                    && parser.getName().equals(TAG_VALUE)) {
-                                values.add(parser.nextText().trim());
-                                count--;
-                            }
-                        }
-                        String [] valueStrings = new String[values.size()];
-                        values.toArray(valueStrings);
-                        restrictions.putStringArray(key, valueStrings);
-                    } else {
-                        String value = parser.nextText().trim();
-                        if (ATTR_TYPE_BOOLEAN.equals(valType)) {
-                            restrictions.putBoolean(key, Boolean.parseBoolean(value));
-                        } else if (ATTR_TYPE_INTEGER.equals(valType)) {
-                            restrictions.putInt(key, Integer.parseInt(value));
-                        } else {
-                            restrictions.putString(key, value);
-                        }
-                    }
-                }
+            while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                readEntry(restrictions, values, parser);
             }
-        } catch (IOException ioe) {
-        } catch (XmlPullParserException pe) {
+        } catch (IOException|XmlPullParserException e) {
+            Log.w(LOG_TAG, "Error parsing " + restrictionsFile.getBaseFile(), e);
         } finally {
-            if (fis != null) {
-                try {
-                    fis.close();
-                } catch (IOException e) {
-                }
-            }
+            IoUtils.closeQuietly(fis);
         }
         return restrictions;
     }
 
+    private static void readEntry(Bundle restrictions, ArrayList<String> values,
+            XmlPullParser parser) throws XmlPullParserException, IOException {
+        int type = parser.getEventType();
+        if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
+            String key = parser.getAttributeValue(null, ATTR_KEY);
+            String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
+            String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
+            if (multiple != null) {
+                values.clear();
+                int count = Integer.parseInt(multiple);
+                while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                    if (type == XmlPullParser.START_TAG
+                            && parser.getName().equals(TAG_VALUE)) {
+                        values.add(parser.nextText().trim());
+                        count--;
+                    }
+                }
+                String [] valueStrings = new String[values.size()];
+                values.toArray(valueStrings);
+                restrictions.putStringArray(key, valueStrings);
+            } else if (ATTR_TYPE_BUNDLE.equals(valType)) {
+                restrictions.putBundle(key, readBundleEntry(parser, values));
+            } else if (ATTR_TYPE_BUNDLE_ARRAY.equals(valType)) {
+                final int outerDepth = parser.getDepth();
+                ArrayList<Bundle> bundleList = new ArrayList<>();
+                while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                    Bundle childBundle = readBundleEntry(parser, values);
+                    bundleList.add(childBundle);
+                }
+                restrictions.putParcelableArray(key,
+                        bundleList.toArray(new Bundle[bundleList.size()]));
+            } else {
+                String value = parser.nextText().trim();
+                if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+                    restrictions.putBoolean(key, Boolean.parseBoolean(value));
+                } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+                    restrictions.putInt(key, Integer.parseInt(value));
+                } else {
+                    restrictions.putString(key, value);
+                }
+            }
+        }
+    }
+
+    private static Bundle readBundleEntry(XmlPullParser parser, ArrayList<String> values)
+            throws IOException, XmlPullParserException {
+        Bundle childBundle = new Bundle();
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            readEntry(childBundle, values, parser);
+        }
+        return childBundle;
+    }
+
     private void writeApplicationRestrictionsLocked(String packageName,
             Bundle restrictions, int userId) {
-        FileOutputStream fos = null;
         AtomicFile restrictionsFile = new AtomicFile(
                 new File(Environment.getUserSystemDirectory(userId),
                         packageToRestrictionsFileName(packageName)));
+        writeApplicationRestrictionsLocked(restrictions, restrictionsFile);
+    }
+
+    @VisibleForTesting
+    static void writeApplicationRestrictionsLocked(Bundle restrictions,
+            AtomicFile restrictionsFile) {
+        FileOutputStream fos = null;
         try {
             fos = restrictionsFile.startWrite();
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
-            // XmlSerializer serializer = XmlUtils.serializerInstance();
             final XmlSerializer serializer = new FastXmlSerializer();
             serializer.setOutput(bos, "utf-8");
             serializer.startDocument(null, true);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             serializer.startTag(null, TAG_RESTRICTIONS);
-
-            for (String key : restrictions.keySet()) {
-                Object value = restrictions.get(key);
-                serializer.startTag(null, TAG_ENTRY);
-                serializer.attribute(null, ATTR_KEY, key);
-
-                if (value instanceof Boolean) {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
-                    serializer.text(value.toString());
-                } else if (value instanceof Integer) {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
-                    serializer.text(value.toString());
-                } else if (value == null || value instanceof String) {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
-                    serializer.text(value != null ? (String) value : "");
-                } else {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
-                    String[] values = (String[]) value;
-                    serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
-                    for (String choice : values) {
-                        serializer.startTag(null, TAG_VALUE);
-                        serializer.text(choice != null ? choice : "");
-                        serializer.endTag(null, TAG_VALUE);
-                    }
-                }
-                serializer.endTag(null, TAG_ENTRY);
-            }
-
+            writeBundle(restrictions, serializer);
             serializer.endTag(null, TAG_RESTRICTIONS);
 
             serializer.endDocument();
             restrictionsFile.finishWrite(fos);
         } catch (Exception e) {
             restrictionsFile.failWrite(fos);
-            Slog.e(LOG_TAG, "Error writing application restrictions list");
+            Slog.e(LOG_TAG, "Error writing application restrictions list", e);
+        }
+    }
+
+    private static void writeBundle(Bundle restrictions, XmlSerializer serializer)
+            throws IOException {
+        for (String key : restrictions.keySet()) {
+            Object value = restrictions.get(key);
+            serializer.startTag(null, TAG_ENTRY);
+            serializer.attribute(null, ATTR_KEY, key);
+
+            if (value instanceof Boolean) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
+                serializer.text(value.toString());
+            } else if (value instanceof Integer) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
+                serializer.text(value.toString());
+            } else if (value == null || value instanceof String) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
+                serializer.text(value != null ? (String) value : "");
+            } else if (value instanceof Bundle) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE);
+                writeBundle((Bundle) value, serializer);
+            } else if (value instanceof Parcelable[]) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE_ARRAY);
+                Parcelable[] array = (Parcelable[]) value;
+                for (Parcelable parcelable : array) {
+                    if (!(parcelable instanceof Bundle)) {
+                        throw new IllegalArgumentException("bundle-array can only hold Bundles");
+                    }
+                    serializer.startTag(null, TAG_ENTRY);
+                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE);
+                    writeBundle((Bundle) parcelable, serializer);
+                    serializer.endTag(null, TAG_ENTRY);
+                }
+            } else {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
+                String[] values = (String[]) value;
+                serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
+                for (String choice : values) {
+                    serializer.startTag(null, TAG_VALUE);
+                    serializer.text(choice != null ? choice : "");
+                    serializer.endTag(null, TAG_VALUE);
+                }
+            }
+            serializer.endTag(null, TAG_ENTRY);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index a33ee4c..c8fd82e 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -136,7 +136,9 @@
 
     public void onTouchStart() {
         synchronized (mLock) {
-            mSensorEventListener.onTouchStartLocked();
+            if (mSensorEventListener != null) {
+                mSensorEventListener.onTouchStartLocked();
+            }
         }
     }
 
@@ -144,7 +146,9 @@
         long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
 
         synchronized (mLock) {
-            mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+            if (mSensorEventListener != null) {
+                mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dcd233f..9b3a772 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4610,19 +4610,19 @@
                     " hidden=" + wtoken.hidden + " hiddenRequested=" +
                     wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
 
+            mOpeningApps.remove(wtoken);
+            mClosingApps.remove(wtoken);
+            wtoken.waitingToShow = wtoken.waitingToHide = false;
+            wtoken.hiddenRequested = !visible;
+
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
             if (okToDisplay() && mAppTransition.isTransitionSet()) {
-                wtoken.hiddenRequested = !visible;
-
                 if (!wtoken.startingDisplayed) {
                     if (DEBUG_APP_TRANSITIONS) Slog.v(
                             TAG, "Setting dummy animation on: " + wtoken);
                     wtoken.mAppAnimator.setDummyAnimation();
                 }
-                mOpeningApps.remove(wtoken);
-                mClosingApps.remove(wtoken);
-                wtoken.waitingToShow = wtoken.waitingToHide = false;
                 wtoken.inPendingTransaction = true;
                 if (visible) {
                     mOpeningApps.add(wtoken);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 090c0f8..53da75b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -53,6 +53,7 @@
 import com.android.server.accounts.AccountManagerService;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.audio.AudioService;
+import com.android.server.camera.CameraService;
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.content.ContentService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -408,6 +409,7 @@
         AudioService audioService = null;
         MmsServiceBroker mmsService = null;
         EntropyMixer entropyMixer = null;
+        CameraService cameraService = null;
 
         boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
         boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
@@ -436,6 +438,9 @@
 
             mContentResolver = context.getContentResolver();
 
+            Slog.i(TAG, "Camera Service");
+            mSystemServiceManager.startService(CameraService.class);
+
             // The AccountManager must come before the ContentService
             try {
                 // TODO: seems like this should be disable-able, but req'd by ContentService
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
new file mode 100644
index 0000000..eb7eb15
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.server.pm;
+
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Parcelable;
+import android.test.AndroidTestCase;
+import android.util.AtomicFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+public class UserManagerServiceTest extends AndroidTestCase {
+    private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
+    private File restrictionsFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
+        restrictionsFile.delete();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        restrictionsFile.delete();
+        super.tearDown();
+    }
+
+    public void testWriteReadApplicationRestrictions() throws IOException {
+        AtomicFile atomicFile = new AtomicFile(restrictionsFile);
+        Bundle bundle = createBundle();
+        UserManagerService.writeApplicationRestrictionsLocked(bundle, atomicFile);
+        assertTrue(atomicFile.getBaseFile().exists());
+        String s = FileUtils.readTextFile(restrictionsFile, 10000, "");
+        System.out.println("restrictionsFile: " + s);
+        bundle = UserManagerService.readApplicationRestrictionsLocked(atomicFile);
+        System.out.println("readApplicationRestrictionsLocked bundle: " + bundle);
+        assertBundle(bundle);
+    }
+
+    private Bundle createBundle() {
+        Bundle result = new Bundle();
+        // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
+        result.putBoolean("boolean_0", false);
+        result.putBoolean("boolean_1", true);
+        result.putInt("integer", 100);
+        result.putString("empty", "");
+        result.putString("string", "text");
+        result.putStringArray("string[]", STRING_ARRAY);
+
+        Bundle bundle = new Bundle();
+        bundle.putString("bundle_string", "bundle_string");
+        bundle.putInt("bundle_int", 1);
+        result.putBundle("bundle", bundle);
+
+        Bundle[] bundleArray = new Bundle[2];
+        bundleArray[0] = new Bundle();
+        bundleArray[0].putString("bundle_array_string", "bundle_array_string");
+        bundleArray[0].putBundle("bundle_array_bundle", bundle);
+        bundleArray[1] = new Bundle();
+        bundleArray[1].putString("bundle_array_string2", "bundle_array_string2");
+        result.putParcelableArray("bundle_array", bundleArray);
+        return result;
+    }
+
+    private void assertBundle(Bundle bundle) {
+        assertFalse(bundle.getBoolean("boolean_0"));
+        assertTrue(bundle.getBoolean("boolean_1"));
+        assertEquals(100, bundle.getInt("integer"));
+        assertEquals("", bundle.getString("empty"));
+        assertEquals("text", bundle.getString("string"));
+        assertEquals(Arrays.asList(STRING_ARRAY), Arrays.asList(bundle.getStringArray("string[]")));
+        Parcelable[] bundle_array = bundle.getParcelableArray("bundle_array");
+        assertEquals(2, bundle_array.length);
+        Bundle bundle1 = (Bundle) bundle_array[0];
+        assertEquals("bundle_array_string", bundle1.getString("bundle_array_string"));
+        assertNotNull(bundle1.getBundle("bundle_array_bundle"));
+        Bundle bundle2 = (Bundle) bundle_array[1];
+        assertEquals("bundle_array_string2", bundle2.getString("bundle_array_string2"));
+        Bundle childBundle = bundle.getBundle("bundle");
+        assertEquals("bundle_string", childBundle.getString("bundle_string"));
+        assertEquals(1, childBundle.getInt("bundle_int"));
+    }
+
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fbc70db..6bc6de63 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3316,11 +3316,11 @@
      * @return the preferred network type, defined in RILConstants.java.
      * @hide
      */
-    public int getPreferredNetworkType() {
+    public int getPreferredNetworkType(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.getPreferredNetworkType();
+                return telephony.getPreferredNetworkType(subId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -3330,6 +3330,27 @@
     }
 
     /**
+     * Sets the network selection mode to automatic.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @hide
+     */
+    public void setNetworkSelectionModeAutomatic(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setNetworkSelectionModeAutomatic(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeAutomatic NPE", ex);
+        }
+    }
+
+    /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
      * <p>
@@ -3337,15 +3358,16 @@
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
+     * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type, defined in RILConstants.java.
      * @return true on success; false on any failure.
      * @hide
      */
-    public boolean setPreferredNetworkType(int networkType) {
+    public boolean setPreferredNetworkType(int subId, int networkType) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.setPreferredNetworkType(networkType);
+                return telephony.setPreferredNetworkType(subId, networkType);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -3364,7 +3386,8 @@
      * @return true on success; false on any failure.
      */
     public boolean setPreferredNetworkTypeToGlobal() {
-        return setPreferredNetworkType(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+        return setPreferredNetworkType(getDefaultSubscription(),
+                RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
     }
 
     /**
@@ -4448,4 +4471,22 @@
 
         return retval;
     }
+
+    /**
+     * Resets telephony manager settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset(int subId) {
+        if (SubscriptionManager.isUsableSubIdValue(subId)) {
+            // Enable data
+            setDataEnabled(subId, true);
+            // Set network selection mode to automatic
+            setNetworkSelectionModeAutomatic(subId);
+            // Set preferred mobile network type to the best available
+            setPreferredNetworkType(subId, RILConstants.PREFERRED_NETWORK_MODE);
+            // Turn off roaming
+            SubscriptionManager.from(mContext).setDataRoaming(0, subId);
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c18e3b6..a24859b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -653,9 +653,10 @@
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
+     * @param subId the id of the subscription to query.
      * @return the preferred network type, defined in RILConstants.java.
      */
-    int getPreferredNetworkType();
+    int getPreferredNetworkType(int subId);
 
     /**
      * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
@@ -667,13 +668,21 @@
     int getTetherApnRequired();
 
     /**
+     * Set the network selection mode to automatic.
+     *
+     * @param subId the id of the subscription to update.
+     */
+    void setNetworkSelectionModeAutomatic(int subId);
+
+    /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
+     * @param subId the id of the subscription to update.
      * @param networkType the preferred network type, defined in RILConstants.java.
      * @return true on success; false on any failure.
      */
-    boolean setPreferredNetworkType(int networkType);
+    boolean setPreferredNetworkType(int subId, int networkType);
 
     /**
      * User enable/disable Mobile Data.
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e1460ef..6371891 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2621,4 +2621,25 @@
         }
         return false;
     }
+
+    /**
+     * Resets all wifi manager settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset() {
+        // Enable wifi
+        setWifiEnabled(true);
+        // Delete all Wifi SSIDs
+        List<WifiConfiguration> networks = getConfiguredNetworks();
+        if (networks != null) {
+            for (WifiConfiguration config : networks) {
+                removeNetwork(config.networkId);
+            }
+            saveConfiguration();
+        }
+
+        // Turn mobile hotspot off
+        setWifiApEnabled(null, false);
+    }
 }