Merge "Make IMMS#mSettingsObserver multiuser-aware." into mnc-dev
diff --git a/Android.mk b/Android.mk
index d6dac53..4d8a37c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -555,8 +555,6 @@
 	frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
 	frameworks/base/core/java/android/speech/tts/Voice.aidl \
 	frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
-	frameworks/base/core/java/android/app/AssistStructure.aidl \
-	frameworks/base/core/java/android/app/AssistContent.aidl \
 	frameworks/base/core/java/android/app/Notification.aidl \
 	frameworks/base/core/java/android/app/NotificationManager.aidl \
 	frameworks/base/core/java/android/app/WallpaperInfo.aidl \
@@ -566,6 +564,8 @@
 	frameworks/base/core/java/android/app/AlarmManager.aidl \
 	frameworks/base/core/java/android/app/SearchableInfo.aidl \
 	frameworks/base/core/java/android/app/VoiceInteractor.aidl \
+	frameworks/base/core/java/android/app/assist/AssistContent.aidl \
+	frameworks/base/core/java/android/app/assist/AssistStructure.aidl \
 	frameworks/base/core/java/android/app/job/JobParameters.aidl \
 	frameworks/base/core/java/android/app/job/JobInfo.aidl \
 	frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3c878c8..0467b69 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3445,7 +3445,7 @@
     method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
     method public boolean onPrepareOptionsMenu(android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
-    method public void onProvideAssistContent(android.app.AssistContent);
+    method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
@@ -3996,26 +3996,19 @@
     field public java.lang.String serviceDetails;
   }
 
-  public class AssistContent implements android.os.Parcelable {
+  public deprecated class AssistContent {
     ctor public AssistContent();
-    method public int describeContents();
     method public android.content.ClipData getClipData();
-    method public android.content.Intent getIntent();
     method public android.net.Uri getWebUri();
     method public void setClipData(android.content.ClipData);
     method public void setIntent(android.content.Intent);
     method public void setWebUri(android.net.Uri);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
   }
 
-  public final class AssistStructure implements android.os.Parcelable {
-    method public int describeContents();
+  public deprecated class AssistStructure {
+    ctor public AssistStructure();
     method public android.content.ComponentName getActivityComponent();
-    method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
     method public int getWindowNodeCount();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
   }
 
   public static class AssistStructure.ViewNode {
@@ -4384,6 +4377,7 @@
     method public void setSharedElementReturnTransition(android.transition.Transition);
     method public void setTargetFragment(android.app.Fragment, int);
     method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
     method public void startActivity(android.content.Intent);
     method public void startActivity(android.content.Intent, android.os.Bundle);
     method public void startActivityForResult(android.content.Intent, int);
@@ -5899,6 +5893,26 @@
 
 }
 
+package android.app.assist {
+
+  public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable {
+    ctor public AssistContent(android.os.Parcel);
+    method public int describeContents();
+    method public android.content.Intent getIntent();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+  }
+
+  public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable {
+    ctor public AssistStructure();
+    method public int describeContents();
+    method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR;
+  }
+
+}
+
 package android.app.backup {
 
   public abstract class BackupAgent extends android.content.ContextWrapper {
@@ -13191,6 +13205,7 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
+    method public abstract boolean isConstrainedHighSpeed();
     method public abstract boolean isReprocessable();
     method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -13311,6 +13326,8 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
@@ -13441,7 +13458,7 @@
     field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
     field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
     field public static final int CONTROL_SCENE_MODE_HDR = 18; // 0x12
-    field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
+    field public static final deprecated int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
     field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
     field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
     field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -13488,6 +13505,7 @@
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -28930,7 +28948,7 @@
     method public android.view.View onCreateContentView();
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(java.lang.String[]);
-    method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
+    method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -32454,7 +32472,7 @@
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
-    method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+    method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
   }
 
   public abstract interface TextDirectionHeuristic {
@@ -33984,6 +34002,7 @@
     method public void setTo(android.util.DisplayMetrics);
     method public void setToDefaults();
     field public static final int DENSITY_280 = 280; // 0x118
+    field public static final int DENSITY_360 = 360; // 0x168
     field public static final int DENSITY_400 = 400; // 0x190
     field public static final int DENSITY_560 = 560; // 0x230
     field public static final int DENSITY_DEFAULT = 160; // 0xa0
diff --git a/api/system-current.txt b/api/system-current.txt
index 65ca29a..d11fe85 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3531,7 +3531,7 @@
     method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
     method public boolean onPrepareOptionsMenu(android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
-    method public void onProvideAssistContent(android.app.AssistContent);
+    method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
@@ -4092,26 +4092,19 @@
     field public java.lang.String serviceDetails;
   }
 
-  public class AssistContent implements android.os.Parcelable {
+  public deprecated class AssistContent {
     ctor public AssistContent();
-    method public int describeContents();
     method public android.content.ClipData getClipData();
-    method public android.content.Intent getIntent();
     method public android.net.Uri getWebUri();
     method public void setClipData(android.content.ClipData);
     method public void setIntent(android.content.Intent);
     method public void setWebUri(android.net.Uri);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
   }
 
-  public final class AssistStructure implements android.os.Parcelable {
-    method public int describeContents();
+  public deprecated class AssistStructure {
+    ctor public AssistStructure();
     method public android.content.ComponentName getActivityComponent();
-    method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
     method public int getWindowNodeCount();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
   }
 
   public static class AssistStructure.ViewNode {
@@ -4480,6 +4473,7 @@
     method public void setSharedElementReturnTransition(android.transition.Transition);
     method public void setTargetFragment(android.app.Fragment, int);
     method public void setUserVisibleHint(boolean);
+    method public boolean shouldShowRequestPermissionRationale(java.lang.String);
     method public void startActivity(android.content.Intent);
     method public void startActivity(android.content.Intent, android.os.Bundle);
     method public void startActivityForResult(android.content.Intent, int);
@@ -6014,6 +6008,26 @@
 
 }
 
+package android.app.assist {
+
+  public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable {
+    ctor public AssistContent(android.os.Parcel);
+    method public int describeContents();
+    method public android.content.Intent getIntent();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+  }
+
+  public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable {
+    ctor public AssistStructure();
+    method public int describeContents();
+    method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR;
+  }
+
+}
+
 package android.app.backup {
 
   public abstract class BackupAgent extends android.content.ContextWrapper {
@@ -13509,6 +13523,7 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
+    method public abstract boolean isConstrainedHighSpeed();
     method public abstract boolean isReprocessable();
     method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -13629,6 +13644,8 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
@@ -13759,7 +13776,7 @@
     field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
     field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
     field public static final int CONTROL_SCENE_MODE_HDR = 18; // 0x12
-    field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
+    field public static final deprecated int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
     field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
     field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
     field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -13806,6 +13823,7 @@
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -31063,7 +31081,7 @@
     method public android.view.View onCreateContentView();
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(java.lang.String[]);
-    method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
+    method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -34719,7 +34737,7 @@
     method public android.text.StaticLayout.Builder setLineSpacing(float, float);
     method public android.text.StaticLayout.Builder setMaxLines(int);
     method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
-    method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+    method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
   }
 
   public abstract interface TextDirectionHeuristic {
@@ -36249,6 +36267,7 @@
     method public void setTo(android.util.DisplayMetrics);
     method public void setToDefaults();
     field public static final int DENSITY_280 = 280; // 0x118
+    field public static final int DENSITY_360 = 360; // 0x168
     field public static final int DENSITY_400 = 400; // 0x190
     field public static final int DENSITY_560 = 560; // 0x230
     field public static final int DENSITY_DEFAULT = 160; // 0xa0
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 250b002..cdf7429 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 
@@ -39,8 +40,10 @@
       (new Telecom()).run(args);
     }
 
-    private static final String COMMAND_SET_PHONE_ACOUNT_ENABLED = "set-phone-account-enabled";
-    private static final String COMMAND_SET_PHONE_ACOUNT_DISABLED = "set-phone-account-disabled";
+    private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
+    private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
+    private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
+    private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
     private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
 
@@ -54,6 +57,8 @@
                 "usage: telecom [subcommand] [options]\n" +
                 "usage: telecom set-phone-account-enabled <COMPONENT> <ID>\n" +
                 "usage: telecom set-phone-account-disabled <COMPONENT> <ID>\n" +
+                "usage: telecom register-phone-account <COMPONENT> <ID> <LABEL>\n" +
+                "usage: telecom unregister-phone-account <COMPONENT> <ID>\n" +
                 "usage: telecom set-default-dialer <PACKAGE>\n" +
                 "usage: telecom get-default-dialer <PACKAGE>\n" +
                 "\n" +
@@ -63,9 +68,9 @@
                 "telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
                 " has already been registered with telecom.\n" +
                 "\n" +
-                "telecom set-default_dialer: Sets the default dialer to the given component. \n" +
+                "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
                 "\n" +
-                "telecom get-default_dialer: Displays the current default dialer. \n"
+                "telecom get-default-dialer: Displays the current default dialer. \n"
                 );
     }
 
@@ -80,12 +85,18 @@
 
         String command = nextArgRequired();
         switch (command) {
-            case COMMAND_SET_PHONE_ACOUNT_ENABLED:
+            case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
                 runSetPhoneAccountEnabled(true);
                 break;
-            case COMMAND_SET_PHONE_ACOUNT_DISABLED:
+            case COMMAND_SET_PHONE_ACCOUNT_DISABLED:
                 runSetPhoneAccountEnabled(false);
                 break;
+            case COMMAND_REGISTER_PHONE_ACCOUNT:
+                runRegisterPhoneAccount();
+                break;
+            case COMMAND_UNREGISTER_PHONE_ACCOUNT:
+                runUnregisterPhoneAccount();
+                break;
             case COMMAND_SET_DEFAULT_DIALER:
                 runSetDefaultDialer();
                 break;
@@ -98,9 +109,7 @@
     }
 
     private void runSetPhoneAccountEnabled(boolean enabled) throws RemoteException {
-        final ComponentName component = parseComponentName(nextArgRequired());
-        final String accountId = nextArgRequired();
-        final PhoneAccountHandle handle = new PhoneAccountHandle(component, accountId);
+        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
         final boolean success =  mTelecomService.enablePhoneAccount(handle, enabled);
         if (success) {
             System.out.println("Success - " + handle + (enabled ? " enabled." : " disabled."));
@@ -109,6 +118,21 @@
         }
     }
 
+    private void runRegisterPhoneAccount() throws RemoteException {
+        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+        final String label = nextArgRequired();
+        PhoneAccount account = PhoneAccount.builder(handle, label)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
+        mTelecomService.registerPhoneAccount(account);
+        System.out.println("Success - " + handle + " registered.");
+    }
+
+    private void runUnregisterPhoneAccount() throws RemoteException {
+        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+        mTelecomService.unregisterPhoneAccount(handle);
+        System.out.println("Success - " + handle + " unregistered.");
+    }
+
     private void runSetDefaultDialer() throws RemoteException {
         final String packageName = nextArgRequired();
         final boolean success = mTelecomService.setDefaultDialer(packageName);
@@ -124,6 +148,12 @@
         System.out.println(mTelecomService.getDefaultDialerPackage());
     }
 
+    private PhoneAccountHandle getPhoneAccountHandleFromArgs() {
+        final ComponentName component = parseComponentName(nextArgRequired());
+        final String accountId = nextArgRequired();
+        return new PhoneAccountHandle(component, accountId);
+    }
+
     private ComponentName parseComponentName(String component) {
         ComponentName cn = ComponentName.unflattenFromString(component);
         if (cn == null) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 073fefc..90567c7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -38,6 +38,7 @@
 
 import android.annotation.SystemApi;
 import android.app.admin.DevicePolicyManager;
+import android.app.assist.AssistContent;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e4def1e..b6cec60 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -17,6 +17,8 @@
 package android.app;
 
 import android.app.ActivityManager.StackInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2a98b6c..3224d41 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index f271af1..4cb89a8 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -26,9 +26,11 @@
 /**
  * Holds information about the content an application is viewing, to hand to an
  * assistant at the user's request.  This is filled in by
- * {@link Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ * @deprecated use {@link android.app.assist.AssistContent}.
  */
-public class AssistContent implements Parcelable {
+@Deprecated
+public class AssistContent {
     private Intent mIntent;
     private ClipData mClipData;
     private Uri mUri;
@@ -36,16 +38,16 @@
     /**
      * @hide
      * Key name this data structure is stored in the Bundle generated by
-     * {@link Activity#onProvideAssistData}.
+     * {@link android.app.Activity#onProvideAssistData}.
      */
     public static final String ASSIST_KEY = "android:assist_content";
 
     /**
      * @hide
      * Retrieve the framework-generated AssistContent that is stored within
-     * the Bundle filled in by {@link Activity#onProvideAssistContent}.
+     * the Bundle filled in by {@link android.app.Activity#onProvideAssistContent}.
      */
-    public static AssistContent getAssistContent(Bundle assistBundle) {
+    public static android.app.assist.AssistContent getAssistContent(Bundle assistBundle) {
         return assistBundle.getParcelable(ASSIST_KEY);
     }
 
@@ -71,6 +73,7 @@
 
     /**
      * Return the current {@link #setIntent}, which you can modify in-place.
+     * @hide
      */
     public Intent getIntent() {
         return mIntent;
@@ -116,7 +119,8 @@
         return mUri;
     }
 
-    AssistContent(Parcel in) {
+    /** @hide */
+    public AssistContent(Parcel in) {
         if (in.readInt() != 0) {
             mIntent = Intent.CREATOR.createFromParcel(in);
         }
@@ -128,13 +132,8 @@
         }
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         if (mIntent != null) {
             dest.writeInt(1);
             mIntent.writeToParcel(dest, flags);
@@ -154,15 +153,4 @@
             dest.writeInt(0);
         }
     }
-
-    public static final Parcelable.Creator<AssistContent> CREATOR
-            = new Parcelable.Creator<AssistContent>() {
-        public AssistContent createFromParcel(Parcel in) {
-            return new AssistContent(in);
-        }
-
-        public AssistContent[] newArray(int size) {
-            return new AssistContent[size];
-        }
-    };
 }
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index ca47a5e..ef7fde4 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -17,9 +17,7 @@
 package android.app;
 
 import android.content.ComponentName;
-import android.graphics.Paint;
 import android.graphics.Rect;
-import android.graphics.Typeface;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -29,7 +27,6 @@
 import android.os.PooledStringWriter;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.text.TextPaint;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -42,19 +39,22 @@
 
 /**
  * Assist data automatically created by the platform's implementation
- * of {@link Activity#onProvideAssistData}.
+ * of {@link android.app.Activity#onProvideAssistData}.
+ * @deprecated use {@link android.app.assist.AssistStructure}.
  */
-final public class AssistStructure implements Parcelable {
+@Deprecated
+public class AssistStructure {
     static final String TAG = "AssistStructure";
 
     /**
      * @hide
      * Key name this data structure is stored in the Bundle generated by
-     * {@link Activity#onProvideAssistData}.
+     * {@link android.app.Activity#onProvideAssistData}.
      */
     public static final String ASSIST_KEY = "android:assist_structure";
 
-    boolean mHaveData;
+    /** @hide */
+    public boolean mHaveData;
 
     ComponentName mActivityComponent;
 
@@ -62,15 +62,18 @@
 
     final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
 
-    SendChannel mSendChannel;
-    IBinder mReceiveChannel;
+    /** @hide */
+    public SendChannel mSendChannel;
+    /** @hide */
+    public IBinder mReceiveChannel;
 
     Rect mTmpRect = new Rect();
 
     static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
     static final String DESCRIPTOR = "android.app.AssistStructure";
 
-    final class SendChannel extends Binder {
+    /** @hide */
+    public final class SendChannel extends Binder {
         @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                 throws RemoteException {
             if (code == TRANSACTION_XFER) {
@@ -702,7 +705,8 @@
         }
     }
 
-    AssistStructure(Activity activity) {
+    /** @hide */
+    public AssistStructure(Activity activity) {
         mHaveData = true;
         mActivityComponent = activity.getComponentName();
         ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
@@ -713,12 +717,13 @@
         }
     }
 
-    AssistStructure() {
+    public AssistStructure() {
         mHaveData = true;
         mActivityComponent = null;
     }
 
-    AssistStructure(Parcel in) {
+    /** @hide */
+    public AssistStructure(Parcel in) {
         mReceiveChannel = in.readStrongBinder();
     }
 
@@ -792,7 +797,7 @@
      * Retrieve the framework-generated AssistStructure that is stored within
      * the Bundle filled in by {@link Activity#onProvideAssistData}.
      */
-    public static AssistStructure getAssistStructure(Bundle assistBundle) {
+    public static android.app.assist.AssistStructure getAssistStructure(Bundle assistBundle) {
         return assistBundle.getParcelable(ASSIST_KEY);
     }
 
@@ -812,16 +817,13 @@
     /**
      * Return one of the windows in the assist data.
      * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
+     * @hide
      */
     public WindowNode getWindowNodeAt(int index) {
         ensureData();
         return mWindowNodes.get(index);
     }
 
-    public int describeContents() {
-        return 0;
-    }
-
     /** @hide */
     public void ensureData() {
         if (mHaveData) {
@@ -880,29 +882,4 @@
         }
         //dump();
     }
-
-    public void writeToParcel(Parcel out, int flags) {
-        if (mHaveData) {
-            // This object holds its data.  We want to write a send channel that the
-            // other side can use to retrieve that data.
-            if (mSendChannel == null) {
-                mSendChannel = new SendChannel();
-            }
-            out.writeStrongBinder(mSendChannel);
-        } else {
-            // This object doesn't hold its data, so just propagate along its receive channel.
-            out.writeStrongBinder(mReceiveChannel);
-        }
-    }
-
-    public static final Parcelable.Creator<AssistStructure> CREATOR
-            = new Parcelable.Creator<AssistStructure>() {
-        public AssistStructure createFromParcel(Parcel in) {
-            return new AssistStructure(in);
-        }
-
-        public AssistStructure[] newArray(int size) {
-            return new AssistStructure[size];
-        }
-    };
 }
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 1fb88a9..02e26a5 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -1107,6 +1107,7 @@
                         }
 
                         if (enterTransition != null) {
+                            enterTransition.removeTarget(state.nonExistentView);
                             View view = inFragment.getView();
                             if (view != null) {
                                 view.captureTransitioningViews(enteringViews);
@@ -1115,7 +1116,6 @@
                                 }
                                 enteringViews.add(state.nonExistentView);
                                 // We added this earlier to prevent any views being targeted.
-                                enterTransition.removeTarget(state.nonExistentView);
                                 addTargets(enterTransition, enteringViews);
                             }
                             setSharedElementEpicenter(enterTransition, state);
@@ -1170,7 +1170,7 @@
             Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
             boolean isBack) {
         boolean overlap = true;
-        if (enterTransition != null && exitTransition != null) {
+        if (enterTransition != null && exitTransition != null && inFragment != null) {
             overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
                     inFragment.getAllowEnterTransitionOverlap();
         }
@@ -1638,7 +1638,7 @@
 
     private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
             boolean isEnd) {
-        int count = mSharedElementTargetNames.size();
+        int count = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
         for (int i = 0; i < count; i++) {
             String source = mSharedElementSourceNames.get(i);
             String originalTarget = mSharedElementTargetNames.get(i);
@@ -1656,7 +1656,7 @@
 
     private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
             boolean isEnd) {
-        int count = namedViews.size();
+        int count = namedViews == null ? 0 : namedViews.size();
         for (int i = 0; i < count; i++) {
             String source = namedViews.keyAt(i);
             String target = namedViews.valueAt(i).getTransitionName();
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 40c5c64..26d4fd4 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1223,6 +1223,33 @@
     }
 
     /**
+     * Gets whether you should show UI with rationale for requesting a permission.
+     * You should do this only if you do not have the permission and the context in
+     * which the permission is requested does not clearly communicate to the user
+     * what would be the benefit from granting this permission.
+     * <p>
+     * For example, if you write a camera app, requesting the camera permission
+     * would be expected by the user and no rationale for why it is requested is
+     * needed. If however, the app needs location for tagging photos then a non-tech
+     * savvy user may wonder how location is related to taking photos. In this case
+     * you may choose to show UI with rationale of requesting this permission.
+     * </p>
+     *
+     * @param permission A permission your app wants to request.
+     * @return Whether you can show permission rationale UI.
+     *
+     * @see Context#checkSelfPermission(String)
+     * @see #requestPermissions(String[], int)
+     * @see #onRequestPermissionsResult(int, String[], int[])
+     */
+    public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
+        if (mHost != null) {
+            mHost.getContext().getPackageManager().shouldShowRequestPermissionRationale(permission);
+        }
+        return false;
+    }
+
+    /**
      * @hide Hack so that DialogFragment can make its Dialog before creating
      * its views, and the view construction can use the dialog's context for
      * inflation.  Maybe this should become a public API. Note sure.
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0a425ae..249cdb2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.RunningServiceInfo;
 import android.app.ActivityManager.StackInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.content.ComponentName;
 import android.content.ContentProviderNative;
 import android.content.IContentProvider;
diff --git a/core/java/android/app/AssistContent.aidl b/core/java/android/app/assist/AssistContent.aidl
similarity index 95%
rename from core/java/android/app/AssistContent.aidl
rename to core/java/android/app/assist/AssistContent.aidl
index a6321bf..24379bb 100644
--- a/core/java/android/app/AssistContent.aidl
+++ b/core/java/android/app/assist/AssistContent.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.app;
+package android.app.assist;
 
 parcelable AssistContent;
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
new file mode 100644
index 0000000..c7e7330
--- /dev/null
+++ b/core/java/android/app/assist/AssistContent.java
@@ -0,0 +1,44 @@
+package android.app.assist;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * New home for AssistContent.
+ */
+public final class AssistContent extends android.app.AssistContent implements Parcelable {
+
+    /** @hide */
+    public AssistContent() {
+    }
+
+    public AssistContent(Parcel in) {
+        super(in);
+    }
+
+    public Intent getIntent() {
+        return super.getIntent();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    public static final Parcelable.Creator<AssistContent> CREATOR
+            = new Parcelable.Creator<AssistContent>() {
+        public AssistContent createFromParcel(Parcel in) {
+            return new AssistContent(in);
+        }
+
+        public AssistContent[] newArray(int size) {
+            return new AssistContent[size];
+        }
+    };
+}
diff --git a/core/java/android/app/AssistStructure.aidl b/core/java/android/app/assist/AssistStructure.aidl
similarity index 95%
rename from core/java/android/app/AssistStructure.aidl
rename to core/java/android/app/assist/AssistStructure.aidl
index 07fb2453..ae0a34c 100644
--- a/core/java/android/app/AssistStructure.aidl
+++ b/core/java/android/app/assist/AssistStructure.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.app;
+package android.app.assist;
 
 parcelable AssistStructure;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
new file mode 100644
index 0000000..1677e95
--- /dev/null
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -0,0 +1,56 @@
+package android.app.assist;
+
+import android.app.Activity;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * New home for AssistStructure.
+ */
+public final class AssistStructure extends android.app.AssistStructure implements Parcelable {
+
+    public AssistStructure() {
+    }
+
+    /** @hide */
+    public AssistStructure(Activity activity) {
+        super(activity);
+    }
+
+    AssistStructure(Parcel in) {
+        super(in);
+    }
+
+    public WindowNode getWindowNodeAt(int index) {
+        return super.getWindowNodeAt(index);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        if (mHaveData) {
+            // This object holds its data.  We want to write a send channel that the
+            // other side can use to retrieve that data.
+            if (mSendChannel == null) {
+                mSendChannel = new SendChannel();
+            }
+            out.writeStrongBinder(mSendChannel);
+        } else {
+            // This object doesn't hold its data, so just propagate along its receive channel.
+            out.writeStrongBinder(mReceiveChannel);
+        }
+    }
+
+    public static final Parcelable.Creator<AssistStructure> CREATOR
+            = new Parcelable.Creator<AssistStructure>() {
+        public AssistStructure createFromParcel(Parcel in) {
+            return new AssistStructure(in);
+        }
+
+        public AssistStructure[] newArray(int size) {
+            return new AssistStructure[size];
+        }
+    };
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 17a8eb7..96a80e7 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1552,23 +1552,21 @@
      *
      * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
      * for a whole class of content.
-     * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
-     * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
-     * specified by <em>uri</em> will cause notifications to be sent. If <code>true</code>, any URI values
-     * at or below the specified URI will also trigger a match.
+     * @param notifyForDescendents When false, the observer will be notified whenever a
+     * change occurs to the exact URI specified by <code>uri</code> or to one of the
+     * URI's ancestors in the path hierarchy.  When true, the observer will also be notified
+     * whenever a change occurs to the URI's descendants in the path hierarchy.
      * @param observer The object that receives callbacks when changes occur.
      * @see #unregisterContentObserver
      */
     public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
-            ContentObserver observer)
-    {
+            ContentObserver observer) {
         registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId());
     }
 
     /** @hide - designated user version */
     public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
-            ContentObserver observer, int userHandle)
-    {
+            ContentObserver observer, int userHandle) {
         try {
             getContentService().registerContentObserver(uri, notifyForDescendents,
                     observer.getContentObserver(), userHandle);
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index c22ee5f..82d40d3 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -471,6 +471,17 @@
     public abstract boolean isReprocessable();
 
     /**
+     * Return if this capture session is constrained high speed session that is created by
+     * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}.
+     *
+     * @return {@code true} if this session is constrained high speed capture session,
+     *         {@code false} otherwise.
+     *
+     * @see CameraDevice#createConstrainedHighSpeedCaptureSession
+     */
+    public abstract boolean isConstrainedHighSpeed();
+
+    /**
      * Get the input Surface associated with a reprocessable capture session.
      *
      * <p>Each reprocessable capture session has an input {@link Surface} where the reprocess
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d5867a9..b69ca88 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -626,35 +626,54 @@
             new Key<Integer>("android.control.maxRegionsAf", int.class);
 
     /**
-     * <p>List of available high speed video size and fps range configurations
-     * supported by the camera device, in the format of (width, height, fps_min, fps_max).</p>
-     * <p>When HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}, this metadata
-     * will list the supported high speed video size and fps range configurations. All the sizes
-     * listed in this configuration will be a subset of the sizes reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } for processed
-     * non-stalling formats.</p>
-     * <p>For the high speed video use case, where the application will set
-     * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the application must
+     * <p>List of available high speed video size, fps range and max batch size configurations
+     * supported by the camera device, in the format of (width, height, fps_min, fps_max, batch_size_max).</p>
+     * <p>When CONSTRAINED_HIGH_SPEED_VIDEO is supported in android.control.availableCapabilities,
+     * this metadata will list the supported high speed video size, fps range and max batch size
+     * configurations. All the sizes listed in this configuration will be a subset of the sizes
+     * reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes }
+     * for processed non-stalling formats.</p>
+     * <p>For the high speed video use case, the application must
      * select the video size and fps range from this metadata to configure the recording and
      * preview streams and setup the recording requests. For example, if the application intends
      * to do high speed recording, it can select the maximum size reported by this metadata to
      * configure output streams. Once the size is selected, application can filter this metadata
      * by selected size and get the supported fps ranges, and use these fps ranges to setup the
      * recording requests. Note that for the use case of multiple output streams, application
-     * must select one unique size from this metadata to use. Otherwise a request error might
-     * occur.</p>
-     * <p>For normal video recording use case, where some application will NOT set
-     * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the fps ranges
-     * reported in this metadata must not be used to setup capture requests, or it will cause
-     * request error.</p>
+     * must select one unique size from this metadata to use (e.g., preview and recording streams
+     * must have the same size). Otherwise, the high speed capture session creation will fail.</p>
+     * <p>The min and max fps will be multiple times of 30fps.</p>
+     * <p>High speed video streaming extends significant performance pressue to camera hardware,
+     * to achieve efficient high speed streaming, the camera device may have to aggregate
+     * multiple frames together and send to camera device for processing where the request
+     * controls are same for all the frames in this batch. Max batch size indicates
+     * the max possible number of frames the camera device will group together for this high
+     * speed stream configuration. This max batch size will be used to generate a high speed
+     * recording request list by
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.
+     * The max batch size for each configuration will satisfy below conditions:</p>
+     * <ul>
+     * <li>Each max batch size will be a divisor of its corresponding fps_max / 30. For example,
+     * if max_fps is 300, max batch size will only be 1, 2, 5, or 10.</li>
+     * <li>The camera device may choose smaller internal batch size for each configuration, but
+     * the actual batch size will be a divisor of max batch size. For example, if the max batch
+     * size is 8, the actual batch size used by camera device will only be 1, 2, 4, or 8.</li>
+     * <li>The max batch size in each configuration entry must be no larger than 32.</li>
+     * </ul>
+     * <p>The camera device doesn't have to support batch mode to achieve high speed video recording,
+     * in such case, batch_size_max will be reported as 1 in each configuration entry.</p>
+     * <p>This fps ranges in this configuration list can only be used to create requests
+     * that are submitted to a high speed camera capture session created by
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }.
+     * The fps ranges reported in this metadata must not be used to setup capture requests for
+     * normal capture session, or it will cause request error.</p>
      * <p><b>Range of valid values:</b><br></p>
-     * <p>For each configuration, the fps_max &gt;= 60fps.</p>
+     * <p>For each configuration, the fps_max &gt;= 120fps.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Limited capability</b> -
      * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
-     * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
-     * @see CaptureRequest#CONTROL_SCENE_MODE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @hide
      */
@@ -1003,12 +1022,33 @@
 
     /**
      * <p>Position of the camera optical center.</p>
-     * <p>As measured in the device sensor coordinate system, the
-     * position of the camera device's optical center, as a
-     * three-dimensional vector <code>(x,y,z)</code>.</p>
-     * <p>To transform a world position to a camera-device centered
-     * coordinate system, the position must be translated by this
-     * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+     * <p>The position of the camera device's lens optical center,
+     * as a three-dimensional vector <code>(x,y,z)</code>, relative to the
+     * optical center of the largest camera device facing in the
+     * same direction as this camera, in the {@link android.hardware.SensorEvent Android sensor coordinate
+     * axes}. Note that only the axis definitions are shared with
+     * the sensor coordinate system, but not the origin.</p>
+     * <p>If this device is the largest or only camera device with a
+     * given facing, then this position will be <code>(0, 0, 0)</code>; a
+     * camera device with a lens optical center located 3 cm from
+     * the main sensor along the +X axis (to the right from the
+     * user's perspective) will report <code>(0.03, 0, 0)</code>.</p>
+     * <p>To transform a pixel coordinates between two cameras
+     * facing the same direction, first the source camera
+     * android.lens.radialDistortion must be corrected for.  Then
+     * the source camera android.lens.intrinsicCalibration needs
+     * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}
+     * of the source camera, the translation of the source camera
+     * relative to the destination camera, the
+     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and
+     * finally the inverse of android.lens.intrinsicCalibration
+     * of the destination camera. This obtains a
+     * radial-distortion-free coordinate in the destination
+     * camera pixel coordinates.</p>
+     * <p>To compare this against a real image from the destination
+     * camera, the destination camera image then needs to be
+     * corrected for radial distortion before comparison or
+     * sampling.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
@@ -1308,7 +1348,8 @@
      * the max pipeline depth.</p>
      * <p>A pipeline depth of X stages is equivalent to a pipeline latency of
      * X frame intervals.</p>
-     * <p>This value will be 8 or less.</p>
+     * <p>This value will normally be 8 or less, however, for high speed capture session,
+     * the max pipeline depth will be up to 8 x size of high speed capture request list.</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureResult#REQUEST_PIPELINE_DEPTH
@@ -1371,6 +1412,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1384,6 +1426,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
      * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index d02f349..006030c 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -583,6 +583,147 @@
             throws CameraAccessException;
 
     /**
+     * <p>Create a new constrained high speed capture session.</p>
+     *
+     * <p>The application can use normal capture session (created via {@link #createCaptureSession})
+     * for high speed capture if the desired high speed FPS ranges are advertised by
+     * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}, in which case all API
+     * semantics associated with normal capture sessions applies.</p>
+     *
+     * <p>The method creates a specialized capture session that is only targeted at high speed
+     * video recording (>=120fps) use case if the camera device supports high speed video
+     * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
+     * Therefore, it has special characteristics compared with a normal capture session:</p>
+     *
+     * <ul>
+     *
+     * <li>In addition to the output target Surface requirements specified by the
+     *   {@link #createCaptureSession} method, an active high speed capture session will support up
+     *   to 2 output Surfaces, though the application might choose to configure just one Surface
+     *   (e.g., preview only). All Surfaces must be either video encoder surfaces (acquired by
+     *   {@link android.media.MediaRecorder#getSurface} or
+     *   {@link android.media.MediaCodec#createInputSurface}) or preview surfaces (obtained from
+     *   {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture} via
+     *   {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). The Surface sizes
+     *   must be one of the sizes reported by {@link StreamConfigurationMap#getHighSpeedVideoSizes}.
+     *   When multiple Surfaces are configured, their size must be same.</li>
+     *
+     * <li>An active high speed capture session only accepts request lists created via
+     *   {@link #createConstrainedHighSpeedRequestList}, and the request list can only be submitted
+     *   to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or
+     *   {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</li>
+     *
+     * <li>The FPS ranges being requested to this session must be selected from
+     *   {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor}. The application can still use
+     *   {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} to control the desired FPS range.
+     *   Switching to an FPS range that has different
+     *   {@link android.util.Range#getUpper() maximum FPS} may trigger some camera device
+     *   reconfigurations, which may introduce extra latency. It is recommended that the
+     *   application avoids unnecessary maximum target FPS changes as much as possible during high
+     *   speed streaming.</li>
+     *
+     * <li>For the request lists submitted to this session, the camera device will override the
+     *   {@link CaptureRequest#CONTROL_MODE control mode}, auto-exposure (AE), auto-white balance
+     *   (AWB) and auto-focus (AF) to {@link CameraMetadata#CONTROL_MODE_AUTO},
+     *   {@link CameraMetadata#CONTROL_AE_MODE_ON}, {@link CameraMetadata#CONTROL_AWB_MODE_AUTO}
+     *   and {@link CameraMetadata#CONTROL_AF_MODE_CONTINUOUS_VIDEO}, respectively. All
+     *   post-processing block mode controls will be overridden to be FAST. Therefore, no manual
+     *   control of capture and post-processing parameters is possible. Beside these, only a subset
+     *   of controls will work, see
+     *   {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO} for
+     *   more details.</li>
+     *
+     * </ul>
+     *
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured high speed image data.
+     * @param callback The callback to notify about the status of the new capture session.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+     *                                  the callback is null, or the handler is null but the current
+     *                                  thread has no looper, or the camera device doesn't support
+     *                                  high speed video capability.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #createCaptureSession
+     * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+     * @see StreamConfigurationMap#getHighSpeedVideoSizes
+     * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+     * @see CameraCaptureSession#captureBurst
+     * @see CameraCaptureSession#setRepeatingBurst
+     * @see #createConstrainedHighSpeedRequestList
+     */
+    public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
+            @NonNull CameraCaptureSession.StateCallback callback,
+            @Nullable Handler handler)
+            throws CameraAccessException;
+
+
+    /**
+     * <p>Create a unmodifiable list of requests that is suitable for constrained high speed capture
+     * session streaming.</p>
+     *
+     * <p>High speed video streaming creates significant performance pressue on the camera device,
+     * so to achieve efficient high speed streaming, the camera device may have to aggregate
+     * multiple frames together. This means requests must be sent in batched groups, with all
+     * requests sharing the same settings. This method takes the list of output target
+     * Surfaces (subject to the output Surface requirements specified by the contrained high speed
+     * session) and a {@link CaptureRequest request}, and generates a request list that has the same
+     * controls for each request. The input {@link CaptureRequest request} must contain the target
+     * output Surfaces and target high speed FPS range that is one of the
+     * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor} for the Surface size.</p>
+     *
+     * <p>If both preview and recording Surfaces are specified in the {@code request}, the
+     * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE target FPS range} in the input
+     * {@link CaptureRequest request} must be a fixed framerate FPS range, where the
+     * {@link android.util.Range#getLower minimal FPS} ==
+     * {@link android.util.Range#getUpper() maximum FPS}. The created request list will contain
+     * a interleaved request pattern such that the preview output FPS is at least 30fps, the
+     * recording output FPS is {@link android.util.Range#getUpper() maximum FPS} of the requested
+     * FPS range. The application can submit this request list directly to an active high speed
+     * capture session to achieve high speed video recording. When only preview or recording
+     * Surface is specified, this method will return a list of request that have the same controls
+     * and output targets for all requests.</p>
+     *
+     * <p>Submitting a request list created by this method to a normal capture session will result
+     * in an {@link IllegalArgumentException} if the high speed
+     * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} is not supported by
+     * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}.</p>
+     *
+     * @param request The high speed capture request that will be used to generate the high speed
+     *                request list.
+     * @return A unmodifiable CaptureRequest list that is suitable for constrained high speed
+     *         capture.
+     *
+     * @throws IllegalArgumentException if the set of output Surfaces in the request do not meet the
+     *                                  high speed video capability requirements, or the camera
+     *                                  device doesn't support high speed video capability, or the
+     *                                  request doesn't meet the high speed video capability
+     *                                  requirements, or the request doesn't contain the required
+     *                                  controls for high speed capture.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #createConstrainedHighSpeedCaptureSession
+     * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+     * @see StreamConfigurationMap#getHighSpeedVideoSizes
+     * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+     */
+    @NonNull
+    public abstract List<CaptureRequest> createConstrainedHighSpeedRequestList(
+            @NonNull CaptureRequest request)throws CameraAccessException;
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
      * initialized with template for a target use case. The settings are chosen
      * to be the best options for the specific camera device, so it is not
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 0f002a9..0fb6889 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -802,12 +802,9 @@
          */
         public ICameraService getCameraService() {
             synchronized(mLock) {
+                connectCameraServiceLocked();
                 if (mCameraService == null) {
-                    Log.i(TAG, "getCameraService: Reconnecting to camera service");
-                    connectCameraServiceLocked();
-                    if (mCameraService == null) {
-                        Log.e(TAG, "Camera service is unavailable");
-                    }
+                    Log.e(TAG, "Camera service is unavailable");
                 }
                 return mCameraService;
             }
@@ -815,11 +812,16 @@
 
         /**
          * Connect to the camera service if it's available, and set up listeners.
+         * If the service is already connected, do nothing.
          *
          * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
          */
         private void connectCameraServiceLocked() {
-            mCameraService = null;
+            // Only reconnect if necessary
+            if (mCameraService != null) return;
+
+            Log.i(TAG, "Connecting to camera service");
+
             IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
             if (cameraServiceBinder == null) {
                 // Camera service is now down, leave mCameraService as null
@@ -1098,6 +1100,8 @@
          */
         public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
             synchronized (mLock) {
+                connectCameraServiceLocked();
+
                 Handler oldHandler = mCallbackMap.put(callback, handler);
                 // For new callbacks, provide initial availability information
                 if (oldHandler == null) {
@@ -1120,6 +1124,8 @@
 
         public void registerTorchCallback(TorchCallback callback, Handler handler) {
             synchronized(mLock) {
+                connectCameraServiceLocked();
+
                 Handler oldHandler = mTorchCallbackMap.put(callback, handler);
                 // For new callbacks, provide initial torch information
                 if (oldHandler == null) {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ac29f80..f8db6d9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -648,6 +648,100 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
 
+    /**
+     * <p>The device supports constrained high speed video recording (frame rate &gt;=120fps)
+     * use case. The camera device will support high speed capture session created by
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
+     * only accepts high speed request list created by
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.</p>
+     * <p>A camera device can still support high speed video streaming by advertising the high speed
+     * FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all normal
+     * capture request per frame control and synchronization requirements will apply to
+     * the high speed fps ranges, the same as all other fps ranges. This capability describes
+     * the capability of a specialized operating mode with many limitations (see below), which
+     * is only targeted at high speed video recording.</p>
+     * <p>The supported high speed video sizes and fps ranges are specified in
+     * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
+     * To get desired output frame rates, the application is only allowed to select video size
+     * and FPS range combinations provided by
+     * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.
+     * The fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
+     * <p>In this capability, the camera device will override aeMode, awbMode, and afMode to
+     * ON, ON, and CONTINUOUS_VIDEO, respectively. All post-processing block mode
+     * controls will be overridden to be FAST. Therefore, no manual control of capture
+     * and post-processing parameters is possible. All other controls operate the
+     * same as when {@link CaptureRequest#CONTROL_MODE android.control.mode} == AUTO. This means that all other
+     * android.control.* fields continue to work, such as</p>
+     * <ul>
+     * <li>{@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}</li>
+     * <li>{@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation}</li>
+     * <li>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</li>
+     * <li>{@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</li>
+     * <li>{@link CaptureRequest#CONTROL_EFFECT_MODE android.control.effectMode}</li>
+     * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+     * <li>{@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}</li>
+     * <li>{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}</li>
+     * </ul>
+     * <p>Outside of android.control.*, the following controls will work:</p>
+     * <ul>
+     * <li>{@link CaptureRequest#FLASH_MODE android.flash.mode} (TORCH mode only, automatic flash for still capture will not
+     * work since aeMode is ON)</li>
+     * <li>{@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} (if it is supported)</li>
+     * <li>{@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}</li>
+     * <li>{@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} (if it is supported)</li>
+     * </ul>
+     * <p>For high speed recording use case, the actual maximum supported frame rate may
+     * be lower than what camera can output, depending on the destination Surfaces for
+     * the image data. For example, if the destination surface is from video encoder,
+     * the application need check if the video encoder is capable of supporting the
+     * high frame rate for a given video size, or it will end up with lower recording
+     * frame rate. If the destination surface is from preview window, the actual preview frame
+     * rate will be bounded by the screen refresh rate.</p>
+     * <p>The camera device will only support up to 2 high speed simultaneous output surfaces
+     * (preview and recording surfaces)
+     * in this mode. Above controls will be effective only if all of below conditions are true:</p>
+     * <ul>
+     * <li>The application creates a camera capture session with no more than 2 surfaces via
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }. The
+     * targeted surfaces must be preview surface (either from
+     * {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or
+     * recording surface(either from {@link android.media.MediaRecorder#getSurface } or
+     * {@link android.media.MediaCodec#createInputSurface }).</li>
+     * <li>The stream sizes are selected from the sizes reported by
+     * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.</li>
+     * <li>The FPS ranges are selected from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
+     * </ul>
+     * <p>When above conditions are NOT satistied, the
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+     * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList } will fail.</p>
+     * <p>Switching to a FPS range that has different maximum FPS may trigger some camera device
+     * reconfigurations, which may introduce extra latency. It is recommended that
+     * the application avoids unnecessary maximum target FPS changes as much as possible
+     * during high speed streaming.</p>
+     *
+     * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+     * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
+     * @see CaptureRequest#CONTROL_AE_LOCK
+     * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+     * @see CaptureRequest#CONTROL_AE_REGIONS
+     * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CaptureRequest#CONTROL_AF_TRIGGER
+     * @see CaptureRequest#CONTROL_AWB_LOCK
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
+     * @see CaptureRequest#CONTROL_EFFECT_MODE
+     * @see CaptureRequest#CONTROL_MODE
+     * @see CaptureRequest#FLASH_MODE
+     * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
+     * @see CaptureRequest#SCALER_CROP_REGION
+     * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -1725,6 +1819,10 @@
     public static final int CONTROL_SCENE_MODE_BARCODE = 16;
 
     /**
+     * <p>This is deprecated, please use
+     * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+     * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }
+     * for high speed video recording.</p>
      * <p>Optimized for high speed video recording (frame rate &gt;=60fps) use case.</p>
      * <p>The supported high speed video sizes and fps ranges are specified in
      * android.control.availableHighSpeedVideoConfigurations. To get desired
@@ -1799,6 +1897,7 @@
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
      * @see CaptureRequest#CONTROL_SCENE_MODE
+     * @deprecated Please refer to this API documentation to find the alternatives
      */
     public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17;
 
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index df6c986..3bb2fdb 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2584,12 +2584,33 @@
 
     /**
      * <p>Position of the camera optical center.</p>
-     * <p>As measured in the device sensor coordinate system, the
-     * position of the camera device's optical center, as a
-     * three-dimensional vector <code>(x,y,z)</code>.</p>
-     * <p>To transform a world position to a camera-device centered
-     * coordinate system, the position must be translated by this
-     * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+     * <p>The position of the camera device's lens optical center,
+     * as a three-dimensional vector <code>(x,y,z)</code>, relative to the
+     * optical center of the largest camera device facing in the
+     * same direction as this camera, in the {@link android.hardware.SensorEvent Android sensor coordinate
+     * axes}. Note that only the axis definitions are shared with
+     * the sensor coordinate system, but not the origin.</p>
+     * <p>If this device is the largest or only camera device with a
+     * given facing, then this position will be <code>(0, 0, 0)</code>; a
+     * camera device with a lens optical center located 3 cm from
+     * the main sensor along the +X axis (to the right from the
+     * user's perspective) will report <code>(0.03, 0, 0)</code>.</p>
+     * <p>To transform a pixel coordinates between two cameras
+     * facing the same direction, first the source camera
+     * android.lens.radialDistortion must be corrected for.  Then
+     * the source camera android.lens.intrinsicCalibration needs
+     * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}
+     * of the source camera, the translation of the source camera
+     * relative to the destination camera, the
+     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and
+     * finally the inverse of android.lens.intrinsicCalibration
+     * of the destination camera. This obtains a
+     * radial-distortion-free coordinate in the destination
+     * camera pixel coordinates.</p>
+     * <p>To compare this against a real image from the destination
+     * camera, the destination camera image then needs to be
+     * corrected for radial distortion before comparison or
+     * sampling.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 7a39dd5..ab0f607 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -721,4 +721,10 @@
         }
     }
 
+    @Override
+    public boolean isConstrainedHighSpeed() {
+        // TODO: to be implemented
+        return false;
+    }
+
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index e60e266..ad0cd0f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -906,7 +906,6 @@
             }
 
             mRemoteDevice = null;
-            mInError = false;
         }
     }
 
@@ -1889,13 +1888,13 @@
     }
 
     private void checkIfCameraClosedOrInError() throws CameraAccessException {
+        if (mRemoteDevice == null) {
+            throw new IllegalStateException("CameraDevice was already closed");
+        }
         if (mInError) {
             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
                     "The camera device has encountered a serious error");
         }
-        if (mRemoteDevice == null) {
-            throw new IllegalStateException("CameraDevice was already closed");
-        }
     }
 
     /** Whether the camera device has started to close (may not yet have finished) */
@@ -1906,4 +1905,18 @@
     private CameraCharacteristics getCharacteristics() {
         return mCharacteristics;
     }
+
+    @Override
+    public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
+            android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException {
+        // TODO: to be implemented
+        throw new UnsupportedOperationException("To be implemented!!!!");
+    }
+
+    @Override
+    public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("To be implemented!!!!");
+    }
 }
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
index c03144b..2449abe 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
@@ -33,7 +33,7 @@
  */
 public class MarshalQueryableHighSpeedVideoConfiguration
         implements MarshalQueryable<HighSpeedVideoConfiguration> {
-    private static final int SIZE = SIZEOF_INT32 * 4;
+    private static final int SIZE = SIZEOF_INT32 * 5;
 
     private class MarshalerHighSpeedVideoConfiguration
             extends Marshaler<HighSpeedVideoConfiguration> {
@@ -49,6 +49,7 @@
             buffer.putInt(value.getHeight());
             buffer.putInt(value.getFpsMin());
             buffer.putInt(value.getFpsMax());
+            buffer.putInt(value.getBatchSizeMax());
         }
 
         @Override
@@ -57,8 +58,9 @@
             int height = buffer.getInt();
             int fpsMin = buffer.getInt();
             int fpsMax = buffer.getInt();
+            int batchSizeMax = buffer.getInt();
 
-            return new HighSpeedVideoConfiguration(width, height, fpsMin, fpsMax);
+            return new HighSpeedVideoConfiguration(width, height, fpsMin, fpsMax, batchSizeMax);
         }
 
         @Override
diff --git a/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java b/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
index 088049f..b469126 100644
--- a/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
+++ b/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
@@ -33,6 +33,7 @@
  * @hide
  */
 public final class HighSpeedVideoConfiguration {
+    static final private int HIGH_SPEED_MAX_MINIMAL_FPS = 120;
 
     /**
      * Create a new {@link HighSpeedVideoConfiguration}.
@@ -48,15 +49,18 @@
      * @hide
      */
     public HighSpeedVideoConfiguration(
-            final int width, final int height, final int fpsMin, final int fpsMax) {
-        if (fpsMax < 60) {
-            throw new IllegalArgumentException("fpsMax must be at least 60");
+            final int width, final int height, final int fpsMin, final int fpsMax,
+            final int batchSizeMax) {
+        if (fpsMax < HIGH_SPEED_MAX_MINIMAL_FPS) {
+            throw new IllegalArgumentException("fpsMax must be at least " +
+                    HIGH_SPEED_MAX_MINIMAL_FPS);
         }
         mFpsMax = fpsMax;
         mWidth = checkArgumentPositive(width, "width must be positive");
         mHeight = checkArgumentPositive(height, "height must be positive");
         mFpsMin = checkArgumentPositive(fpsMin, "fpsMin must be positive");
         mSize = new Size(mWidth, mHeight);
+        mBatchSizeMax = checkArgumentPositive(batchSizeMax, "batchSizeMax must be positive");
         mFpsRange = new Range<Integer>(mFpsMin, mFpsMax);
     }
 
@@ -106,9 +110,18 @@
     }
 
     /**
+     * Convenience method to return the max batch size of this high speed video configuration.
+     *
+     * @return the maximal batch size for this high speed video configuration
+     */
+    public int getBatchSizeMax() {
+        return mBatchSizeMax;
+    }
+
+    /**
      * Convenience method to return the FPS range of this high speed video configuration.
      *
-     * @return a Range with high bound >= 60
+     * @return a Range with high bound >= {@value #HIGH_SPEED_MAX_MINIMAL_FPS}
      */
     public Range<Integer> getFpsRange() {
         return mFpsRange;
@@ -135,7 +148,8 @@
             return mWidth == other.mWidth &&
                     mHeight == other.mHeight &&
                     mFpsMin == other.mFpsMin &&
-                    mFpsMax == other.mFpsMax;
+                    mFpsMax == other.mFpsMax &&
+                    mBatchSizeMax == other.mBatchSizeMax;
         }
         return false;
     }
@@ -152,6 +166,7 @@
     private final int mHeight;
     private final int mFpsMin;
     private final int mFpsMax;
+    private final int mBatchSizeMax;
     private final Size mSize;
     private final Range<Integer> mFpsRange;
 }
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 2097d5a..409542d 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -67,7 +67,8 @@
     OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
         in KeymasterArguments params, in byte[] entropy);
     OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
-    OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature);
+    OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature,
+        in byte[] entropy);
     int abort(IBinder handle);
     boolean isOperationAuthorized(IBinder token);
     int addAuthToken(in byte[] authToken);
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 894edac..8fe84e1 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -16,8 +16,8 @@
 
 package android.service.voice;
 
-import android.app.AssistContent;
-import android.app.AssistStructure;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Bundle;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 48ad5a8..33fef62 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,11 +16,11 @@
 
 package android.service.voice;
 
-import android.app.AssistContent;
-import android.app.AssistStructure;
 import android.app.Dialog;
 import android.app.Instrumentation;
 import android.app.VoiceInteractor;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.Intent;
@@ -386,7 +386,7 @@
         }
 
         /**
-         * ASk the app to cancel this current request.
+         * ASk the app to cancelLocked this current request.
          */
         public void cancel() {
             try {
@@ -878,14 +878,34 @@
         show(null, 0);
     }
 
-    public void show(Bundle args, int showFlags) {
+    /**
+     * Show the UI for this session.  This asks the system to go through the process of showing
+     * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
+     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
+     * @param args Arbitrary arguments that will be propagated {@link #onShow}.
+     * @param flags Indicates additional optional behavior that should be performed.  May
+     * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST}
+     * to request that the system generate and deliver assist data on the current foreground
+     * app as part of showing the session UI.
+     */
+    public void show(Bundle args, int flags) {
+        if (mToken == null) {
+            throw new IllegalStateException("Can't call before onCreate()");
+        }
         try {
-            mSystemService.showSessionFromSession(mToken, null, 0);
+            mSystemService.showSessionFromSession(mToken, args, flags);
         } catch (RemoteException e) {
         }
     }
 
+    /**
+     * Hide the session's UI, if currently shown.  Call this when you are done with your
+     * user interaction.
+     */
     public void hide() {
+        if (mToken == null) {
+            throw new IllegalStateException("Can't call before onCreate()");
+        }
         try {
             mSystemService.hideSessionFromSession(mToken);
         } catch (RemoteException e) {
@@ -964,6 +984,9 @@
      * {@link #startVoiceActivity}.</p>
      */
     public void setKeepAwake(boolean keepAwake) {
+        if (mToken == null) {
+            throw new IllegalStateException("Can't call before onCreate()");
+        }
         try {
             mSystemService.setKeepAwake(mToken, keepAwake);
         } catch (RemoteException e) {
@@ -985,7 +1008,9 @@
     }
 
     /**
-     * Finish the session.
+     * Finish the session.  This completely destroys the session -- the next time it is shown,
+     * an entirely new one will be created.  You do not normally call this function; instead,
+     * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
      */
     public void finish() {
         if (mToken == null) {
@@ -1114,7 +1139,7 @@
      * Called when the user presses the back button while focus is in the session UI.  Note
      * that this will only happen if the session UI has requested input focus in its window;
      * otherwise, the back key will go to whatever window has focus and do whatever behavior
-     * it normally has there.
+     * it normally has there.  The default implementation simply calls {@link #hide}.
      */
     public void onBackPressed() {
         hide();
@@ -1123,7 +1148,7 @@
     /**
      * Sessions automatically watch for requests that all system UI be closed (such as when
      * the user presses HOME), which will appear here.  The default implementation always
-     * calls {@link #finish}.
+     * calls {@link #hide}.
      */
     public void onCloseSystemDialogs() {
         hide();
@@ -1287,7 +1312,7 @@
     }
 
     /**
-     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
+     * Called when the {@link android.app.VoiceInteractor} has asked to cancelLocked a {@link Request}
      * that was previously delivered to {@link #onRequestConfirmation},
      * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
      * or {@link #onRequestCommand}.
diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java
index dbc290b..bb5d3ea 100644
--- a/core/java/android/text/Annotation.java
+++ b/core/java/android/text/Annotation.java
@@ -38,6 +38,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.ANNOTATION;
     }
     
@@ -46,6 +51,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeString(mKey);
         dest.writeString(mValue);
     }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index e99a960..5b5cdd2 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -291,7 +291,7 @@
         b.setText(text, where, where + after)
                 .setPaint(getPaint())
                 .setWidth(getWidth())
-                .setTextDir(getTextDirectionHeuristic())
+                .setTextDirection(getTextDirectionHeuristic())
                 .setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
                 .setEllipsizedWidth(mEllipsizedWidth)
                 .setEllipsize(mEllipsizeAt)
diff --git a/core/java/android/text/ParcelableSpan.java b/core/java/android/text/ParcelableSpan.java
index 224511a..d7c1a4b 100644
--- a/core/java/android/text/ParcelableSpan.java
+++ b/core/java/android/text/ParcelableSpan.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
@@ -27,5 +28,21 @@
     /**
      * Return a special type identifier for this span class.
      */
-    public abstract int getSpanTypeId();
+    int getSpanTypeId();
+
+    /**
+     * Internal implementation of {@link #getSpanTypeId()} that is not meant to
+     * be overridden outside of the framework.
+     *
+     * @hide
+     */
+    int getSpanTypeIdInternal();
+
+    /**
+     * Internal implementation of {@link Parcelable#writeToParcel(Parcel, int)}
+     * that is not meant to be overridden outside of the framework.
+     *
+     * @hide
+     */
+    void writeToParcelInternal(Parcel dest, int flags);
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d6d046b..464710b 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -184,7 +184,7 @@
          * @param textDir text direction heuristic for resolving BiDi behavior.
          * @return this builder, useful for chaining
          */
-        public Builder setTextDir(TextDirectionHeuristic textDir) {
+        public Builder setTextDirection(TextDirectionHeuristic textDir) {
             mTextDir = textDir;
             return this;
         }
@@ -473,7 +473,7 @@
 
         Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
             .setAlignment(align)
-            .setTextDir(textDir)
+            .setTextDirection(textDir)
             .setLineSpacing(spacingadd, spacingmult)
             .setIncludePad(includepad)
             .setEllipsizedWidth(ellipsizedWidth)
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 676986d..6c4d8fd 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -622,8 +622,7 @@
      * Flatten a CharSequence and whatever styles can be copied across processes
      * into the parcel.
      */
-    public static void writeToParcel(CharSequence cs, Parcel p,
-            int parcelableFlags) {
+    public static void writeToParcel(CharSequence cs, Parcel p, int parcelableFlags) {
         if (cs instanceof Spanned) {
             p.writeInt(0);
             p.writeString(cs.toString());
@@ -645,15 +644,15 @@
                 }
 
                 if (prop instanceof ParcelableSpan) {
-                    ParcelableSpan ps = (ParcelableSpan)prop;
-                    int spanTypeId = ps.getSpanTypeId();
+                    final ParcelableSpan ps = (ParcelableSpan) prop;
+                    final int spanTypeId = ps.getSpanTypeIdInternal();
                     if (spanTypeId < FIRST_SPAN || spanTypeId > LAST_SPAN) {
-                        Log.e(TAG, "external class \"" + ps.getClass().getSimpleName()
+                        Log.e(TAG, "External class \"" + ps.getClass().getSimpleName()
                                 + "\" is attempting to use the frameworks-only ParcelableSpan"
                                 + " interface");
                     } else {
                         p.writeInt(spanTypeId);
-                        ps.writeToParcel(p, parcelableFlags);
+                        ps.writeToParcelInternal(p, parcelableFlags);
                         writeWhere(p, sp, o);
                     }
                 }
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 1214040..908ef55 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -49,6 +49,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.ABSOLUTE_SIZE_SPAN;
     }
     
@@ -57,6 +62,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mSize);
         dest.writeInt(mDip ? 1 : 0);
     }
diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java
index b8a37da..6158309 100644
--- a/core/java/android/text/style/AlignmentSpan.java
+++ b/core/java/android/text/style/AlignmentSpan.java
@@ -22,10 +22,9 @@
 import android.text.TextUtils;
 
 public interface AlignmentSpan extends ParagraphStyle {
-    public Layout.Alignment getAlignment();
+    Layout.Alignment getAlignment();
 
-    public static class Standard
-    implements AlignmentSpan, ParcelableSpan {
+    class Standard implements AlignmentSpan, ParcelableSpan {
         public Standard(Layout.Alignment align) {
             mAlignment = align;
         }
@@ -35,6 +34,11 @@
         }
         
         public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
             return TextUtils.ALIGNMENT_SPAN;
         }
         
@@ -43,6 +47,11 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
+            writeToParcelInternal(dest, flags);
+        }
+
+        /** @hide */
+        public void writeToParcelInternal(Parcel dest, int flags) {
             dest.writeString(mAlignment.name());
         }
 
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index cda8015..de05f50 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -35,6 +35,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.BACKGROUND_COLOR_SPAN;
     }
     
@@ -43,6 +48,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
 
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 3f86b08..7408415 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -60,6 +60,11 @@
     }
 
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.BULLET_SPAN;
     }
 
@@ -68,6 +73,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mGapWidth);
         dest.writeInt(mWantColor ? 1 : 0);
         dest.writeInt(mColor);
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index 03b4f60..7af1c2c 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -91,12 +91,22 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeParcelable(mPendingIntent, 0);
         dest.writeByte((byte) (mDeleteEnabled ? 1 : 0));
     }
 
     @Override
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.EASY_EDIT_SPAN;
     }
 
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index f167aab..2bc6d54 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -36,14 +36,24 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.FOREGROUND_COLOR_SPAN;
     }
-    
+
     public int describeContents() {
         return 0;
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
 
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 96a7cd9..339d885 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -125,6 +125,11 @@
         }
         
         public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
             return TextUtils.LEADING_MARGIN_SPAN;
         }
         
@@ -133,6 +138,11 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
+            writeToParcelInternal(dest, flags);
+        }
+
+        /** @hide */
+        public void writeToParcelInternal(Parcel dest, int flags) {
             dest.writeInt(mFirst);
             dest.writeInt(mRest);
         }
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index a12c42f..d286231 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -44,6 +44,11 @@
 
     @Override
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.LOCALE_SPAN;
     }
 
@@ -54,6 +59,11 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeString(mLocale.getLanguage());
         dest.writeString(mLocale.getCountry());
         dest.writeString(mLocale.getVariant());
diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java
index 17748ca..0b0a82c 100644
--- a/core/java/android/text/style/QuoteSpan.java
+++ b/core/java/android/text/style/QuoteSpan.java
@@ -45,6 +45,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.QUOTE_SPAN;
     }
     
@@ -53,6 +58,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
 
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 632dbd4..95f048a 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -34,6 +34,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.RELATIVE_SIZE_SPAN;
     }
     
@@ -42,6 +47,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeFloat(mProportion);
     }
 
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index a22a5a1..d0850185 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -34,6 +34,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.SCALE_X_SPAN;
     }
     
@@ -42,6 +47,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeFloat(mProportion);
     }
 
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index 0d8a103..10275c2 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -54,11 +54,21 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mSpellCheckInProgress ? 1 : 0);
     }
 
     @Override
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.SPELL_CHECK_SPAN;
     }
 }
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 303e41574..1389704 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -30,6 +30,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.STRIKETHROUGH_SPAN;
     }
     
@@ -38,6 +43,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
     }
 
     @Override
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index b08f70e..f900db5 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -50,6 +50,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.STYLE_SPAN;
     }
     
@@ -58,6 +63,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mStyle);
     }
 
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index de1d8b2..f1b0d38 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -29,6 +29,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.SUBSCRIPT_SPAN;
     }
     
@@ -37,6 +42,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
     }
 
     @Override
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index 2dbfc72..c1943d5 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -46,11 +46,21 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mBackgroundColor);
     }
 
     @Override
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.SUGGESTION_RANGE_SPAN;
     }
 
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 8b40953..6b449f9 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -248,6 +248,11 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeStringArray(mSuggestions);
         dest.writeInt(mFlags);
         dest.writeString(mLocaleString);
@@ -264,6 +269,11 @@
 
     @Override
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.SUGGESTION_SPAN;
     }
 
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index 285fe84..abcf688 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -29,6 +29,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.SUPERSCRIPT_SPAN;
     }
     
@@ -37,6 +42,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
     }
 
     @Override
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index ecbf4bc..abbd793 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -136,6 +136,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.TEXT_APPEARANCE_SPAN;
     }
     
@@ -144,6 +149,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeString(mTypeface);
         dest.writeInt(mStyle);
         dest.writeInt(mTextSize);
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index 342a183..c40f11f 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -495,12 +495,22 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeString(mType);
         dest.writePersistableBundle(mArgs);
     }
 
     @Override
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.TTS_SPAN;
     }
 
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index f194060..aa622d8 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -42,6 +42,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.TYPEFACE_SPAN;
     }
     
@@ -50,6 +55,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeString(mFamily);
     }
 
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 0669b6f..58239ef 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -40,6 +40,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.URL_SPAN;
     }
     
@@ -48,6 +53,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeString(mURL);
     }
 
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 80b2427..9024dcd 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -30,6 +30,11 @@
     }
     
     public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    public int getSpanTypeIdInternal() {
         return TextUtils.UNDERLINE_SPAN;
     }
     
@@ -38,6 +43,11 @@
     }
 
     public void writeToParcel(Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
+    }
+
+    /** @hide */
+    public void writeToParcelInternal(Parcel dest, int flags) {
     }
 
     @Override
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index d0e5b9e..a36e66c 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -79,6 +79,14 @@
      * This is not a density that applications should target, instead relying
      * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
      */
+    public static final int DENSITY_360 = 360;
+
+    /**
+     * Intermediate density for screens that sit somewhere between
+     * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+     * This is not a density that applications should target, instead relying
+     * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+     */
     public static final int DENSITY_400 = 400;
 
     /**
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index 83dfc47..c2a6a7a 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -119,7 +119,14 @@
     /** @hide */
     public StateSet() {}
 
+    /**
+     * A state specification that will be matched by all StateSets.
+     */
     public static final int[] WILD_CARD = new int[0];
+
+    /**
+     * A state set that does not contain any valid states.
+     */
     public static final int[] NOTHING = new int[] { 0 };
 
     /**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 6e2d110..664c02a 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -19,6 +19,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -158,7 +159,9 @@
             try {
                 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
                 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
-                if (spec != null) {
+                // Recycle if called from another process. Specs are cached in the
+                // system process and obtained from a pool when read from parcel.
+                if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
                     spec.recycle();
                 }
                 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -167,6 +170,12 @@
             } catch (RemoteException re) {
                 /* ignore - the other side will time out */
             }
+
+            // Recycle if called from the same process. Regions are obtained in
+            // the system process and instantiated  when read from parcel.
+            if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+                interactiveRegion.recycle();
+            }
         }
     }
 
@@ -244,7 +253,9 @@
             try {
                 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
                 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
-                if (spec != null) {
+                // Recycle if called from another process. Specs are cached in the
+                // system process and obtained from a pool when read from parcel.
+                if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
                     spec.recycle();
                 }
                 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -252,6 +263,12 @@
             } catch (RemoteException re) {
                 /* ignore - the other side will time out */
             }
+
+            // Recycle if called from the same process. Regions are obtained in
+            // the system process and instantiated  when read from parcel.
+            if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+                interactiveRegion.recycle();
+            }
         }
     }
 
@@ -354,7 +371,9 @@
             try {
                 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
                 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
-                if (spec != null) {
+                // Recycle if called from another process. Specs are cached in the
+                // system process and obtained from a pool when read from parcel.
+                if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
                     spec.recycle();
                 }
                 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -362,6 +381,12 @@
             } catch (RemoteException re) {
                 /* ignore - the other side will time out */
             }
+
+            // Recycle if called from the same process. Regions are obtained in
+            // the system process and instantiated  when read from parcel.
+            if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+                interactiveRegion.recycle();
+            }
         }
     }
 
@@ -468,7 +493,9 @@
             try {
                 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
                 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
-                if (spec != null) {
+                // Recycle if called from another process. Specs are cached in the
+                // system process and obtained from a pool when read from parcel.
+                if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
                     spec.recycle();
                 }
                 adjustIsVisibleToUserIfNeeded(focused, interactiveRegion);
@@ -476,6 +503,12 @@
             } catch (RemoteException re) {
                 /* ignore - the other side will time out */
             }
+
+            // Recycle if called from the same process. Regions are obtained in
+            // the system process and instantiated  when read from parcel.
+            if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+                interactiveRegion.recycle();
+            }
         }
     }
 
@@ -545,7 +578,9 @@
             try {
                 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
                 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
-                if (spec != null) {
+                // Recycle if called from another process. Specs are cached in the
+                // system process and obtained from a pool when read from parcel.
+                if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
                     spec.recycle();
                 }
                 adjustIsVisibleToUserIfNeeded(next, interactiveRegion);
@@ -553,6 +588,12 @@
             } catch (RemoteException re) {
                 /* ignore - the other side will time out */
             }
+
+            // Recycle if called from the same process. Regions are obtained in
+            // the system process and instantiated  when read from parcel.
+            if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+                interactiveRegion.recycle();
+            }
         }
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 774307f..126540f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17115,6 +17115,7 @@
      * Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator
      * attached to this view.
      */
+    @CallSuper
     public void jumpDrawablesToCurrentState() {
         if (mBackground != null) {
             mBackground.jumpToCurrentState();
@@ -17564,13 +17565,16 @@
      * @see Drawable#setTintMode(PorterDuff.Mode)
      */
     public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTint == null) {
-            mBackgroundTint = new TintInfo();
+        if (mForegroundInfo == null) {
+            mForegroundInfo = new ForegroundInfo();
         }
-        mBackgroundTint.mTintMode = tintMode;
-        mBackgroundTint.mHasTintMode = true;
+        if (mForegroundInfo.mTintInfo == null) {
+            mForegroundInfo.mTintInfo = new TintInfo();
+        }
+        mForegroundInfo.mTintInfo.mTintMode = tintMode;
+        mForegroundInfo.mTintInfo.mHasTintMode = true;
 
-        applyBackgroundTint();
+        applyForegroundTint();
     }
 
     /**
@@ -17580,7 +17584,7 @@
      * @return the blending mode used to apply the tint to the foreground
      *         drawable
      * @attr ref android.R.styleable#View_foregroundTintMode
-     * @see #setBackgroundTintMode(PorterDuff.Mode)
+     * @see #setForegroundTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getForegroundTintMode() {
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 886547a..e525474 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.text.TextPaint;
 
 /**
  * Container for storing additional per-view data generated by {@link View#onProvideStructure
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9e0719d..997e7e8 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -251,7 +251,7 @@
      * @return true if any inset values are nonzero
      */
     public boolean hasInsets() {
-        return hasSystemWindowInsets() || hasWindowDecorInsets();
+        return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets();
     }
 
     /**
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index f7b6405..ca5f5ad 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1058,6 +1058,13 @@
     public boolean isKeyguardSecure();
 
     /**
+     * Return whether the keyguard is on.
+     *
+     * @return true if in keyguard is on.
+     */
+    public boolean isKeyguardShowingOrOccluded();
+
+    /**
      * inKeyguardRestrictedKeyInputMode
      *
      * if keyguard screen is showing or in restricted key input mode (i.e. in
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 260854f..027f6d6 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -52,12 +52,6 @@
  */
 public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
     /**
-     * Contains the list of objects that represent the data of this ArrayAdapter.
-     * The content of this list is referred to as "the array" in the documentation.
-     */
-    private List<T> mObjects;
-
-    /**
      * Lock used to modify the content of {@link #mObjects}. Any write operation
      * performed on the array should be synchronized on this lock. This lock is also
      * used by the filter (see {@link #getFilter()} to make a synchronized copy of
@@ -65,6 +59,14 @@
      */
     private final Object mLock = new Object();
 
+    private final LayoutInflater mInflater;
+
+    /**
+     * Contains the list of objects that represent the data of this ArrayAdapter.
+     * The content of this list is referred to as "the array" in the documentation.
+     */
+    private List<T> mObjects;
+
     /**
      * The resource indicating what views to inflate to display the content of this
      * array adapter.
@@ -97,8 +99,6 @@
     private ArrayList<T> mOriginalValues;
     private ArrayFilter mFilter;
 
-    private LayoutInflater mInflater;
-
     /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
     private LayoutInflater mDropDownInflater;
 
@@ -442,9 +442,6 @@
         return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
         final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index d38a225..7e542c9 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -115,7 +115,8 @@
                 R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
 
         // Set up and attach container.
-        mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
+        mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator, false);
+        mDelegator.addView(mContainer);
 
         // Set up header views.
         final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
@@ -471,7 +472,11 @@
 
     @Override
     public void setEnabled(boolean enabled) {
-        mContainer.setEnabled(false);
+        mContainer.setEnabled(enabled);
+        mDayPickerView.setEnabled(enabled);
+        mYearPickerView.setEnabled(enabled);
+        mHeaderYear.setEnabled(enabled);
+        mHeaderMonthDay.setEnabled(enabled);
     }
 
     @Override
@@ -481,8 +486,7 @@
 
     @Override
     public CalendarView getCalendarView() {
-        throw new UnsupportedOperationException(
-                "CalendarView does not exists for the new DatePicker");
+        throw new UnsupportedOperationException("Not supported by calendar-mode DatePicker");
     }
 
     @Override
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0cac529..56f9b5c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1004,14 +1004,14 @@
                 stopSelectionActionMode();
             } else {
                 stopSelectionActionMode();
-                startSelectionActionModeWithSelectionAndStartDrag();
+                selectCurrentWordAndStartDrag();
             }
             handled = true;
         }
 
         // Start a new selection
         if (!handled) {
-            handled = startSelectionActionModeWithSelectionAndStartDrag();
+            handled = selectCurrentWordAndStartDrag();
         }
 
         return handled;
@@ -1724,22 +1724,9 @@
     }
 
     /**
-     * Starts a Selection Action Mode with the current selection and enters drag mode. This should
-     * be used whenever the mode is started from a touch event.
-     *
-     * @return true if the selection mode was actually started.
-     */
-    private boolean startSelectionActionModeWithSelectionAndStartDrag() {
-        boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
-        if (selectionStarted) {
-            getSelectionController().enterDrag();
-        }
-        return selectionStarted;
-    }
-
-    /**
      * Starts a Selection Action Mode with the current selection and ensures the selection handles
-     * are shown. This should be used when the mode is started from a non-touch event.
+     * are shown if there is a selection, otherwise the insertion handle is shown. This should be
+     * used when the mode is started from a non-touch event.
      *
      * @return true if the selection mode was actually started.
      */
@@ -1747,40 +1734,67 @@
         boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
         if (selectionStarted) {
             getSelectionController().show();
+        } else if (getInsertionController() != null) {
+            getInsertionController().show();
         }
         return selectionStarted;
     }
 
-    private boolean startSelectionActionModeWithSelectionInternal() {
+    /**
+     * If the TextView allows text selection, selects the current word when no existing selection
+     * was available and starts a drag.
+     *
+     * @return true if the drag was started.
+     */
+    private boolean selectCurrentWordAndStartDrag() {
         if (extractedTextModeWillBeStarted()) {
             // Cancel the single tap delayed runnable.
             if (mSelectionModeWithoutSelectionRunnable != null) {
                 mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
             }
+            return false;
+        }
+        if (mSelectionActionMode != null) {
+            mSelectionActionMode.finish();
+        }
+        if (!checkFieldAndSelectCurrentWord()) {
+            return false;
+        }
+        getSelectionController().enterDrag();
+        return true;
+    }
 
+    /**
+     * Checks whether a selection can be performed on the current TextView and if so selects
+     * the current word.
+     *
+     * @return true if there already was a selection or if the current word was selected.
+     */
+    private boolean checkFieldAndSelectCurrentWord() {
+        if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
+            Log.w(TextView.LOG_TAG,
+                    "TextView does not support text selection. Selection cancelled.");
             return false;
         }
 
+        if (!mTextView.hasSelection()) {
+            // There may already be a selection on device rotation
+            return selectCurrentWord();
+        }
+        return true;
+    }
+
+    private boolean startSelectionActionModeWithSelectionInternal() {
         if (mSelectionActionMode != null) {
             // Selection action mode is already started
             mSelectionActionMode.invalidate();
             return false;
         }
 
-        if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
-            Log.w(TextView.LOG_TAG,
-                    "TextView does not support text selection. Action mode cancelled.");
+        if (!checkFieldAndSelectCurrentWord()) {
             return false;
         }
 
-        if (!mTextView.hasSelection()) {
-            // There may already be a selection on device rotation
-            if (!selectCurrentWord()) {
-                // No word found under cursor or text selection not permitted.
-                return false;
-            }
-        }
-
         boolean willExtract = extractedTextModeWillBeStarted();
 
         // Do not start the action mode when extracted text will show up full screen, which would
@@ -1829,12 +1843,18 @@
         }
         if (selectionStart == selectionEnd) {
             // Spans overlap the cursor.
-            return true;
+            for (int i = 0; i < suggestionSpans.length; i++) {
+                if (suggestionSpans[i].getSuggestions().length > 0) {
+                    return true;
+                }
+            }
+            return false;
         }
         int minSpanStart = mTextView.getText().length();
         int maxSpanEnd = 0;
         int unionOfSpansCoveringSelectionStartStart = mTextView.getText().length();
         int unionOfSpansCoveringSelectionStartEnd = 0;
+        boolean hasValidSuggestions = false;
         for (int i = 0; i < suggestionSpans.length; i++) {
             final int spanStart = spannable.getSpanStart(suggestionSpans[i]);
             final int spanEnd = spannable.getSpanEnd(suggestionSpans[i]);
@@ -1844,11 +1864,16 @@
                 // The span doesn't cover the current selection start point.
                 continue;
             }
+            hasValidSuggestions =
+                    hasValidSuggestions || suggestionSpans[i].getSuggestions().length > 0;
             unionOfSpansCoveringSelectionStartStart =
                     Math.min(unionOfSpansCoveringSelectionStartStart, spanStart);
             unionOfSpansCoveringSelectionStartEnd =
                     Math.max(unionOfSpansCoveringSelectionStartEnd, spanEnd);
         }
+        if (!hasValidSuggestions) {
+            return false;
+        }
         if (unionOfSpansCoveringSelectionStartStart >= unionOfSpansCoveringSelectionStartEnd) {
             // No spans cover the selection start point.
             return false;
@@ -4071,12 +4096,17 @@
                     offset = getNextCursorOffset(selectionEnd, false);
                     mTouchWordDelta = 0.0f;
                 }
-                mInWord = !getWordIteratorWithText().isBoundary(offset);
                 positionAtCursorOffset(offset, false);
             }
         }
 
         @Override
+        protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+            super.positionAtCursorOffset(offset, parentScrolled);
+            mInWord = !getWordIteratorWithText().isBoundary(offset);
+        }
+
+        @Override
         public boolean onTouchEvent(MotionEvent event) {
             boolean superResult = super.onTouchEvent(event);
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -4193,12 +4223,17 @@
                     offset = getNextCursorOffset(selectionStart, true);
                     mTouchWordDelta = 0.0f;
                 }
-                mInWord = !getWordIteratorWithText().isBoundary(offset);
                 positionAtCursorOffset(offset, false);
             }
         }
 
         @Override
+        protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+            super.positionAtCursorOffset(offset, parentScrolled);
+            mInWord = !getWordIteratorWithText().isBoundary(offset);
+        }
+
+        @Override
         public boolean onTouchEvent(MotionEvent event) {
             boolean superResult = super.onTouchEvent(event);
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -4377,7 +4412,7 @@
                             boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
 
                             if (stayedInArea && isPositionOnText(eventX, eventY)) {
-                                startSelectionActionModeWithSelectionAndStartDrag();
+                                selectCurrentWordAndStartDrag();
                                 mDiscardNextActionUp = true;
                             }
                         }
@@ -4480,6 +4515,7 @@
                         mEndHandle.showAtLocation(endOffset);
 
                         // No longer the first dragging motion, reset.
+                        startSelectionActionModeWithSelection();
                         mDragAcceleratorActive = false;
                         mStartOffset = -1;
                     }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 16dc26d..e7d9226 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.Widget;
 import android.content.Context;
@@ -608,7 +609,16 @@
 
         mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
 
-        mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
+        final Drawable selectionDivider = attributesArray.getDrawable(
+                R.styleable.NumberPicker_selectionDivider);
+        if (selectionDivider != null) {
+            selectionDivider.setCallback(this);
+            selectionDivider.setLayoutDirection(getLayoutDirection());
+            if (selectionDivider.isStateful()) {
+                selectionDivider.setState(getDrawableState());
+            }
+        }
+        mSelectionDivider = selectionDivider;
 
         final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
                 TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
@@ -1499,6 +1509,38 @@
         removeAllCallbacks();
     }
 
+    @CallSuper
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+
+        final int[] state = getDrawableState();
+
+        if (mSelectionDivider != null && mSelectionDivider.isStateful()) {
+            mSelectionDivider.setState(state);
+        }
+    }
+
+    @CallSuper
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+
+        if (mSelectionDivider != null) {
+            mSelectionDivider.jumpToCurrentState();
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) {
+        super.onResolveDrawables(layoutDirection);
+
+        if (mSelectionDivider != null) {
+            mSelectionDivider.setLayoutDirection(layoutDirection);
+        }
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (!mHasSelectorWheel) {
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index e7760ee..3bf9485 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -52,6 +52,8 @@
  * If no appropriate binding can be found, an {@link IllegalStateException} is thrown.
  */
 public class SimpleAdapter extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
+    private final LayoutInflater mInflater;
+
     private int[] mTo;
     private String[] mFrom;
     private ViewBinder mViewBinder;
@@ -60,7 +62,6 @@
 
     private int mResource;
     private int mDropDownResource;
-    private LayoutInflater mInflater;
 
     /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
     private LayoutInflater mDropDownInflater;
@@ -174,8 +175,8 @@
 
     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        return createViewFromResource(
-                mDropDownInflater, position, convertView, parent, mDropDownResource);
+        final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
+        return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
     }
 
     private void bindView(int position, View view) {
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index c521f72..27fa3b9 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -188,32 +188,9 @@
         mLocalActivityManager = activityGroup;
     }
 
-
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
-        treeObserver.addOnTouchModeChangeListener(this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        final ViewTreeObserver treeObserver = getViewTreeObserver();
-        treeObserver.removeOnTouchModeChangeListener(this);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public void onTouchModeChanged(boolean isInTouchMode) {
-        if (!isInTouchMode) {
-            // leaving touch mode.. if nothing has focus, let's give it to
-            // the indicator of the current tab
-            if (mCurrentView != null && (!mCurrentView.hasFocus() || mCurrentView.isFocused())) {
-                mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
-            }
-        }
+        // No longer used, but kept to maintain API compatibility.
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e14e39c..3a85476 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6680,7 +6680,7 @@
                 StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
                         mHint.length(), mTextPaint, hintWidth)
                         .setAlignment(alignment)
-                        .setTextDir(mTextDir)
+                        .setTextDirection(mTextDir)
                         .setLineSpacing(mSpacingAdd, mSpacingMult)
                         .setIncludePad(mIncludePad)
                         .setBreakStrategy(mBreakStrategy)
@@ -6771,7 +6771,7 @@
             StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
                     0, mTransformed.length(), mTextPaint, wantWidth)
                     .setAlignment(alignment)
-                    .setTextDir(mTextDir)
+                    .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
                     .setBreakStrategy(mBreakStrategy)
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index e02d706..c77d614 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -37,6 +37,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
@@ -83,7 +84,7 @@
     private final Rect mContentRect = new Rect();
 
     private Menu mMenu;
-    private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
+    private List<Object> mShowingMenuItems = new ArrayList<Object>();
     private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
 
     private int mSuggestedWidth;
@@ -155,7 +156,7 @@
         if (!isCurrentlyShowing(menuItems) || mWidthChanged) {
             mPopup.dismiss();
             mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
-            mShowingTitles = getMenuItemTitles(menuItems);
+            mShowingMenuItems = getShowingMenuItemsReferences(menuItems);
         }
         mPopup.updateCoordinates(mContentRect);
         if (!mPopup.isShowing()) {
@@ -210,7 +211,7 @@
      * Returns true if this floating toolbar is currently showing the specified menu items.
      */
     private boolean isCurrentlyShowing(List<MenuItem> menuItems) {
-        return mShowingTitles.equals(getMenuItemTitles(menuItems));
+        return mShowingMenuItems.equals(getShowingMenuItemsReferences(menuItems));
     }
 
     /**
@@ -233,12 +234,16 @@
         return menuItems;
     }
 
-    private List<CharSequence> getMenuItemTitles(List<MenuItem> menuItems) {
-        List<CharSequence> titles = new ArrayList<CharSequence>();
+    private List<Object> getShowingMenuItemsReferences(List<MenuItem> menuItems) {
+        List<Object> references = new ArrayList<Object>();
         for (MenuItem menuItem : menuItems) {
-            titles.add(menuItem.getTitle());
+            if (isIconOnlyMenuItem(menuItem)) {
+                references.add(menuItem.getIcon());
+            } else {
+                references.add(menuItem.getTitle());
+            }
         }
-        return titles;
+        return references;
     }
 
 
@@ -323,6 +328,7 @@
             }
         };
 
+        private final Rect mViewPort = new Rect();
         private final Point mCoords = new Point();
 
         private final Region mTouchableRegion = new Region();
@@ -394,7 +400,7 @@
                 mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow);
             }
             List<MenuItem> overflowMenuItems =
-                    mMainPanel.layoutMenuItems(menuItems, suggestedWidth);
+                    mMainPanel.layoutMenuItems(menuItems, getToolbarWidth(suggestedWidth));
             mMainPanel.setOnMenuItemClickListener(menuItemClickListener);
             if (!overflowMenuItems.isEmpty()) {
                 if (mOverflowPanel == null) {
@@ -430,7 +436,8 @@
                 // The "show" animation will make this visible.
                 mContentContainer.setAlpha(0);
             }
-            updateOverflowHeight(contentRect.top - (mMarginVertical * 2));
+            refreshViewPort();
+            updateOverflowHeight(contentRect.top - (mMarginVertical * 2) - mViewPort.top);
             refreshCoordinatesAndOverflowDirection(contentRect);
             preparePopupContent();
             mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y);
@@ -494,6 +501,7 @@
             }
 
             cancelOverflowAnimations();
+            refreshViewPort();
             refreshCoordinatesAndOverflowDirection(contentRect);
             preparePopupContent();
             mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight());
@@ -521,18 +529,24 @@
         }
 
         private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
+            // NOTE: Ensure that mViewPort has been refreshed before this.
+
             int x = contentRect.centerX() - getWidth() / 2;
             int y;
-            if (contentRect.top > getHeight()) {
+            if (contentRect.top - getHeight() > mViewPort.top) {
                 y = contentRect.top - getHeight();
                 mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP;
-            } else if (contentRect.top > getToolbarHeightWithVerticalMargin()) {
+            } else if (contentRect.top - getToolbarHeightWithVerticalMargin() > mViewPort.top) {
                 y = contentRect.top - getToolbarHeightWithVerticalMargin();
                 mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
             } else {
                 y = contentRect.bottom;
                 mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
             }
+
+            // Update x so that the toolbar isn't rendered behind the nav bar in landscape.
+            x = Math.max(0, Math.min(x, mViewPort.right - getWidth()));
+
             mCoords.set(x, y);
             if (mOverflowPanel != null) {
                 mOverflowPanel.setOverflowDirection(mOverflowDirection);
@@ -821,6 +835,29 @@
             mPopupWindow.setHeight(height + mMarginVertical * 2);
         }
 
+
+        private void refreshViewPort() {
+            mParent.getGlobalVisibleRect(mViewPort);
+            WindowInsets windowInsets = mParent.getRootWindowInsets();
+            mViewPort.set(
+                    mViewPort.left + windowInsets.getStableInsetLeft(),
+                    mViewPort.top + windowInsets.getStableInsetTop(),
+                    mViewPort.right - windowInsets.getStableInsetRight(),
+                    mViewPort.bottom - windowInsets.getStableInsetBottom());
+        }
+
+        private int getToolbarWidth(int suggestedWidth) {
+            int width = suggestedWidth;
+            refreshViewPort();
+            int maximumWidth = mViewPort.width() - 2 * mParent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+            if (width <= 0) {
+                width = mParent.getResources()
+                        .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
+            }
+            return Math.min(width, maximumWidth);
+        }
+
         /**
          * Sets the touchable region of this popup to be zero. This means that all touch events on
          * this popup will go through to the surface behind it.
@@ -906,12 +943,11 @@
          *
          * @return The menu items that are not included in this main panel.
          */
-        public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int suggestedWidth) {
+        public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int width) {
             Preconditions.checkNotNull(menuItems);
 
-            final int toolbarWidth = getAdjustedToolbarWidth(mContext, suggestedWidth)
-                    // Reserve space for the "open overflow" button.
-                    - getEstimatedOpenOverflowButtonWidth(mContext);
+            // Reserve space for the "open overflow" button.
+            final int toolbarWidth = width - getEstimatedOpenOverflowButtonWidth(mContext);
 
             int availableWidth = toolbarWidth;
             final LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 52fd111..3fd3b3c 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -111,19 +111,19 @@
             "()J",
             (void*)android_os_Trace_nativeGetEnabledTags },
     { "nativeTraceCounter",
-            "(JLjava/lang/String;I)V",
+            "!(JLjava/lang/String;I)V",
             (void*)android_os_Trace_nativeTraceCounter },
     { "nativeTraceBegin",
-            "(JLjava/lang/String;)V",
+            "!(JLjava/lang/String;)V",
             (void*)android_os_Trace_nativeTraceBegin },
     { "nativeTraceEnd",
-            "(J)V",
+            "!(J)V",
             (void*)android_os_Trace_nativeTraceEnd },
     { "nativeAsyncTraceBegin",
-            "(JLjava/lang/String;I)V",
+            "!(JLjava/lang/String;I)V",
             (void*)android_os_Trace_nativeAsyncTraceBegin },
     { "nativeAsyncTraceEnd",
-            "(JLjava/lang/String;I)V",
+            "!(JLjava/lang/String;I)V",
             (void*)android_os_Trace_nativeAsyncTraceEnd },
     { "nativeSetAppTracingAllowed",
             "(Z)V",
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3fa92a8..abd2409 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -499,7 +499,7 @@
     nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
     UiFrameInfoBuilder(proxy->frameInfo())
             .setVsync(vsync, vsync)
-            .addFlag(FrameInfoFlags::kSurfaceCanvas);
+            .addFlag(FrameInfoFlags::SurfaceCanvas);
     proxy->syncAndDrawFrame();
 }
 
diff --git a/core/res/res/drawable/number_picker_divider_material.xml b/core/res/res/drawable/number_picker_divider_material.xml
new file mode 100644
index 0000000..2474be0
--- /dev/null
+++ b/core/res/res/drawable/number_picker_divider_material.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:tint="?attr/colorControlNormal"
+       android:shape="rectangle">
+    <solid android:color="@color/black" />
+</shape>
diff --git a/core/res/res/layout/number_picker_material.xml b/core/res/res/layout/number_picker_material.xml
new file mode 100644
index 0000000..47edec4
--- /dev/null
+++ b/core/res/res/layout/number_picker_material.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+      class="android.widget.NumberPicker$CustomEditText"
+      android:id="@+id/numberpicker_input"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:gravity="center"
+      android:singleLine="true"
+      android:background="@null"
+      android:textAppearance="@style/TextAppearance.Material.Caption" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index f7a42fa..7782ed7 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -630,13 +630,13 @@
     </style>
 
     <style name="Widget.Material.NumberPicker" parent="Widget.NumberPicker">
-        <item name="internalLayout">@layout/number_picker_with_selector_wheel</item>
+        <item name="internalLayout">@layout/number_picker_material</item>
         <item name="solidColor">@color/transparent</item>
-        <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
-        <item name="selectionDividerHeight">2dip</item>
-        <item name="selectionDividersDistance">48dip</item>
-        <item name="internalMinWidth">64dip</item>
-        <item name="internalMaxHeight">180dip</item>
+        <item name="selectionDivider">@drawable/number_picker_divider_material</item>
+        <item name="selectionDividerHeight">2dp</item>
+        <item name="selectionDividersDistance">48dp</item>
+        <item name="internalMinWidth">64dp</item>
+        <item name="internalMaxHeight">180dp</item>
         <item name="virtualButtonPressedDrawable">?attr/selectableItemBackground</item>
     </style>
 
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index d6f8cca..7aa0aef 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -587,10 +587,29 @@
     public static final int RAW12 = 0x26;
 
     /**
-     * Android dense depth image format.
+     * <p>Android dense depth image format.</p>
      *
-     * Each pixel is 16 bits, representing a depth ranging measurement from
-     * a depth camera or similar sensor.
+     * <p>Each pixel is 16 bits, representing a depth ranging measurement from a depth camera or
+     * similar sensor. The 16-bit sample consists of a confidence value and the actual ranging
+     * measurement.</p>
+     *
+     * <p>The confidence value is an estimate of correctness for this sample.  It is encoded in the
+     * 3 most significant bits of the sample, with a value of 0 representing 100% confidence, a
+     * value of 1 representing 0% confidence, a value of 2 representing 1/7, a value of 3
+     * representing 2/7, and so on.</p>
+     *
+     * <p>As an example, the following sample extracts the range and confidence from the first pixel
+     * of a DEPTH16-format {@link android.media.Image}, and converts the confidence to a
+     * floating-point value between 0 and 1.f inclusive, with 1.f representing maximum confidence:
+     *
+     * <pre>
+     *    ShortBuffer shortDepthBuffer = img.getPlanes()[0].getBuffer().asShortBuffer();
+     *    short depthSample = shortDepthBuffer.get()
+     *    short depthRange = (short) (depthSample & 0x1FFF);
+     *    short depthConfidence = (short) ((depthSample >> 13) & 0x7);
+     *    float depthPercentage = depthConfidence == 0 ? 1.f : (depthConfidence - 1) / 7.f;
+     * </pre>
+     * </p>
      *
      * <p>This format assumes
      * <ul>
@@ -602,19 +621,32 @@
      *
      * <pre> y_size = stride * height </pre>
      *
-     * When produced by a camera, the units are millimeters.
+     * When produced by a camera, the units for the range are millimeters.
      */
     public static final int DEPTH16 = 0x44363159;
 
     /**
      * Android sparse depth point cloud format.
      *
-     * <p>A variable-length list of 3D points, with each point represented
-     * by a triple of floats.</p>
+     * <p>A variable-length list of 3D points plus a confidence value, with each point represented
+     * by four floats; first the X, Y, Z position coordinates, and then the confidence value.</p>
      *
-     * <p>The number of points is {@code (size of the buffer in bytes) / 12}.
+     * <p>The number of points is {@code (size of the buffer in bytes) / 16}.
      *
-     * The coordinate system and units depend on the source of the point cloud data.
+     * <p>The coordinate system and units of the position values depend on the source of the point
+     * cloud data. The confidence value is between 0.f and 1.f, inclusive, with 0 representing 0%
+     * confidence and 1.f representing 100% confidence in the measured position values.</p>
+     *
+     * <p>As an example, the following code extracts the first depth point in a DEPTH_POINT_CLOUD
+     * format {@link android.media.Image}:
+     * <pre>
+     *    FloatBuffer floatDepthBuffer = img.getPlanes()[0].getBuffer().asFloatBuffer();
+     *    float x = floatDepthBuffer.get();
+     *    float y = floatDepthBuffer.get();
+     *    float z = floatDepthBuffer.get();
+     *    float confidence = floatDepthBuffer.get();
+     * </pre>
+     *
      */
     public static final int DEPTH_POINT_CLOUD = 0x101;
 
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 37ed723..1a05104 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -514,15 +514,20 @@
         }
     }
 
-    public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) {
+    public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature,
+            byte[] entropy) {
         try {
-            return mBinder.finish(token, arguments, signature);
+            return mBinder.finish(token, arguments, signature, entropy);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
         }
     }
 
+    public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) {
+        return finish(token, arguments, signature, null);
+    }
+
     public int abort(IBinder token) {
         try {
             return mBinder.abort(token);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index 03be759..e555cc0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -43,13 +43,17 @@
     private static final String PACKAGE_NAME = "android.security.keystore";
     private static final String KEYSTORE_SECRET_KEY_CLASS_NAME =
             PACKAGE_NAME + ".AndroidKeyStoreSecretKey";
+    private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME =
+            PACKAGE_NAME + ".AndroidKeyStorePrivateKey";
+    private static final String KEYSTORE_PUBLIC_KEY_CLASS_NAME =
+            PACKAGE_NAME + ".AndroidKeyStorePublicKey";
 
     AndroidKeyStoreBCWorkaroundProvider() {
         super("AndroidKeyStoreBCWorkaround",
                 1.0,
                 "Android KeyStore security provider to work around Bouncy Castle");
 
-        // javax.crypto.Mac
+        // --------------------- javax.crypto.Mac
         putMacImpl("HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA1");
         put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
         put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1");
@@ -75,7 +79,7 @@
         put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512");
         put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512");
 
-        // javax.crypto.Cipher
+        // --------------------- javax.crypto.Cipher
         putSymmetricCipherImpl("AES/ECB/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$NoPadding");
         putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
@@ -88,6 +92,136 @@
 
         putSymmetricCipherImpl("AES/CTR/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
+
+        putAsymmetricCipherImpl("RSA/ECB/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$NoPadding");
+        put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+        putAsymmetricCipherImpl("RSA/ECB/PKCS1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$PKCS1Padding");
+        put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPPadding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA224AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA256AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA384AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
+        putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-512AndMGF1Padding",
+                PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
+
+        // --------------------- java.security.Signature
+        putSignatureImpl("NONEwithRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$NONEWithPKCS1Padding");
+
+        putSignatureImpl("MD5withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$MD5WithPKCS1Padding");
+        put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5WithRSA");
+        put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSA");
+
+        putSignatureImpl("SHA1withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA1WithRSAEncryption", "SHA1WithRSA");
+        put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSA");
+        put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSA");
+
+        putSignatureImpl("SHA224withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA224WithRSAEncryption", "SHA224WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA224WithRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1",
+                "SHA224WithRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.11",
+                "SHA224WithRSA");
+
+        putSignatureImpl("SHA256withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1",
+                "SHA256WithRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11",
+                "SHA256WithRSA");
+
+        putSignatureImpl("SHA384withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1",
+                "SHA384WithRSA");
+
+        putSignatureImpl("SHA512withRSA",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPKCS1Padding");
+        put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512WithRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1",
+                "SHA512WithRSA");
+
+        putSignatureImpl("SHA1withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPSSPadding");
+        putSignatureImpl("SHA224withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPSSPadding");
+        putSignatureImpl("SHA256withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPSSPadding");
+        putSignatureImpl("SHA384withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPSSPadding");
+        putSignatureImpl("SHA512withRSA/PSS",
+                PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPSSPadding");
+
+        putSignatureImpl("NONEwithECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE");
+
+        putSignatureImpl("ECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
+        put("Alg.Alias.Signature.SHA1withECDSA", "ECDSA");
+        put("Alg.Alias.Signature.ECDSAwithSHA1", "ECDSA");
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
+        put("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "ECDSA");
+
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+        putSignatureImpl("SHA224withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA224");
+        // ecdsa-with-SHA224(1)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA");
+
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+        putSignatureImpl("SHA256withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA256");
+        // ecdsa-with-SHA256(2)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA");
+
+        putSignatureImpl("SHA384withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA384");
+        // ecdsa-with-SHA384(3)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA");
+
+        putSignatureImpl("SHA512withECDSA",
+                PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA512");
+        // ecdsa-with-SHA512(4)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA");
     }
 
     private void putMacImpl(String algorithm, String implClass) {
@@ -99,4 +233,16 @@
         put("Cipher." + transformation, implClass);
         put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
     }
+
+    private void putAsymmetricCipherImpl(String transformation, String implClass) {
+        put("Cipher." + transformation, implClass);
+        put("Cipher." + transformation + " SupportedKeyClasses",
+                KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+    }
+
+    private void putSignatureImpl(String algorithm, String implClass) {
+        put("Signature." + algorithm, implClass);
+        put("Signature." + algorithm + " SupportedKeyClasses",
+                KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+    }
 }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 3ad3c9d..fd9bdb8 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -66,7 +66,7 @@
      */
     private IBinder mOperationToken;
     private long mOperationHandle;
-    private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+    private KeyStoreCryptoOperationStreamer mMainDataStreamer;
 
     /**
      * Encountered exception which could not be immediately thrown because it was encountered inside
@@ -210,7 +210,6 @@
         byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
                 mRng, getAdditionalEntropyAmountForBegin());
 
-        KeymasterArguments keymasterOutputArgs = new KeymasterArguments();
         OperationResult opResult = mKeyStore.begin(
                 mKey.getAlias(),
                 mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
@@ -247,9 +246,21 @@
         }
 
         loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams);
-        mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+        mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token);
+    }
+
+    /**
+     * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives
+     * the corresponding ciphertext/plaintext from the KeyStore.
+     *
+     * <p>This implementation returns a working streamer.
+     */
+    @NonNull
+    protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+            KeyStore keyStore, IBinder operationToken) {
+        return new KeyStoreCryptoOperationChunkedStreamer(
                 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
-                        mKeyStore, opResult.token));
+                        keyStore, operationToken));
     }
 
     @Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
new file mode 100644
index 0000000..335da07
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
@@ -0,0 +1,123 @@
+/*
+ * 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.security.KeyStore;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.SignatureSpi;
+
+/**
+ * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
+
+    public final static class NONE extends AndroidKeyStoreECDSASignatureSpi {
+        public NONE() {
+            super(KeymasterDefs.KM_DIGEST_NONE);
+        }
+    }
+
+    public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA1() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA224() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA256() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA384() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
+        public SHA512() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterDigest;
+
+    private int mGroupSizeBytes = -1;
+
+    AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
+        mKeymasterDigest = keymasterDigest;
+    }
+
+    @Override
+    protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+        if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+                    + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
+        }
+
+        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+        int errorCode = getKeyStore().getKeyCharacteristics(
+                key.getAlias(), null, null, keyCharacteristics);
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode);
+        }
+        int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+        if (keySizeBits == -1) {
+            throw new InvalidKeyException("Size of key not known");
+        }
+        mGroupSizeBytes = (keySizeBits + 7) / 8;
+
+        super.initKey(key);
+    }
+
+    @Override
+    protected final void resetAll() {
+        mGroupSizeBytes = -1;
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected void addAlgorithmSpecificParametersToBegin(
+            @NonNull KeymasterArguments keymasterArgs) {
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+    }
+
+    @Override
+    protected int getAdditionalEntropyAmountForBegin() {
+        return (isSigning()) ? mGroupSizeBytes : 0;
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
new file mode 100644
index 0000000..3ed396d
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
@@ -0,0 +1,57 @@
+/*
+ * 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.security.keystore;
+
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+
+/**
+ * {@link ECPublicKey} backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey implements ECPublicKey {
+
+    private final ECParameterSpec mParams;
+    private final ECPoint mW;
+
+    public AndroidKeyStoreECPublicKey(String alias, byte[] x509EncodedForm, ECParameterSpec params,
+            ECPoint w) {
+        super(alias, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
+        mParams = params;
+        mW = w;
+    }
+
+    public AndroidKeyStoreECPublicKey(String alias, ECPublicKey info) {
+        this(alias, info.getEncoded(), info.getParams(), info.getW());
+        if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+            throw new IllegalArgumentException(
+                    "Unsupported key export format: " + info.getFormat());
+        }
+    }
+
+    @Override
+    public ECParameterSpec getParams() {
+        return mParams;
+    }
+
+    @Override
+    public ECPoint getW() {
+        return mW;
+    }
+}
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
index 6098e5c..1751aa5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
@@ -19,7 +19,7 @@
 import java.security.Key;
 
 /**
- * {@link Key} backed by AndroidKeyStore.
+ * {@link Key} backed by Android Keystore.
  *
  * @hide
  */
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index dc4c8a3..4d6178f 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -296,19 +296,33 @@
         int flags = 0;
         String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
         KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
-        int errorCode = mKeyStore.generateKey(
-                keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics);
-        if (errorCode != KeyStore.NO_ERROR) {
-            throw new ProviderException(
-                    "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
-        }
-        @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
+        boolean success = false;
         try {
-            keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
-                    mKeymasterAlgorithm, mKeymasterDigest);
-        } catch (IllegalArgumentException e) {
-            throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+            Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+            int errorCode = mKeyStore.generateKey(
+                    keyAliasInKeystore,
+                    args,
+                    additionalEntropy,
+                    flags,
+                    resultingKeyCharacteristics);
+            if (errorCode != KeyStore.NO_ERROR) {
+                throw new ProviderException(
+                        "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
+            }
+            @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
+            try {
+                keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
+                        mKeymasterAlgorithm, mKeymasterDigest);
+            } catch (IllegalArgumentException e) {
+                throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+            }
+            SecretKey result = new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+            success = true;
+            return result;
+        } finally {
+            if (!success) {
+                Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias());
+            }
         }
-        return new AndroidKeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
     }
 }
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 4b45fd7..7b5ca3a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -121,7 +121,6 @@
     public KeyPair generateKeyPair() {
         if (mKeyStore == null || mSpec == null) {
             throw new IllegalStateException("Not initialized");
-
         }
 
         final int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0;
@@ -134,62 +133,65 @@
 
         final String alias = mSpec.getKeystoreAlias();
 
-        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-
         byte[][] args = getArgsForKeyType(mKeyType, mSpec.getAlgorithmParameterSpec());
 
         final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
 
-        if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
-                flags, args)) {
-            throw new IllegalStateException("could not generate key in keystore");
-        }
-
-        Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
-
-        final PrivateKey privKey;
-        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+        boolean success = false;
         try {
-            privKey = engine.getPrivateKeyById(privateKeyAlias);
-        } catch (InvalidKeyException e) {
-            throw new RuntimeException("Can't get key", e);
-        }
-
-        final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
-
-        final PublicKey pubKey;
-        try {
-            final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
-            pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
-        } catch (NoSuchAlgorithmException e) {
-            throw new IllegalStateException("Can't instantiate key generator", e);
-        } catch (InvalidKeySpecException e) {
-            throw new IllegalStateException("keystore returned invalid key encoding", e);
-        }
-
-        final X509Certificate cert;
-        try {
-            cert = generateCertificate(privKey, pubKey);
-        } catch (Exception e) {
             Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            throw new IllegalStateException("Can't generate certificate", e);
-        }
+            if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, mKeyType, mKeySize,
+                    flags, args)) {
+                throw new IllegalStateException("could not generate key in keystore");
+            }
 
-        byte[] certBytes;
-        try {
-            certBytes = cert.getEncoded();
-        } catch (CertificateEncodingException e) {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            throw new IllegalStateException("Can't get encoding of certificate", e);
-        }
+            final PrivateKey privKey;
+            final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+            try {
+                privKey = engine.getPrivateKeyById(privateKeyAlias);
+            } catch (InvalidKeyException e) {
+                throw new RuntimeException("Can't get key", e);
+            }
 
-        if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
-                flags)) {
-            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
-            throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
-        }
+            final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
 
-        return new KeyPair(pubKey, privKey);
+            final PublicKey pubKey;
+            try {
+                final KeyFactory keyFact = KeyFactory.getInstance(mKeyAlgorithm);
+                pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
+            } catch (NoSuchAlgorithmException e) {
+                throw new IllegalStateException("Can't instantiate key generator", e);
+            } catch (InvalidKeySpecException e) {
+                throw new IllegalStateException("keystore returned invalid key encoding", e);
+            }
+
+            final X509Certificate cert;
+            try {
+                cert = generateCertificate(privKey, pubKey);
+            } catch (Exception e) {
+                throw new IllegalStateException("Can't generate certificate", e);
+            }
+
+            byte[] certBytes;
+            try {
+                certBytes = cert.getEncoded();
+            } catch (CertificateEncodingException e) {
+                throw new IllegalStateException("Can't get encoding of certificate", e);
+            }
+
+            if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes, KeyStore.UID_SELF,
+                    flags)) {
+                throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
+            }
+
+            KeyPair result = new KeyPair(pubKey, privKey);
+            success = true;
+            return result;
+        } finally {
+            if (!success) {
+                Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+            }
+        }
     }
 
     @SuppressWarnings("deprecation")
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
new file mode 100644
index 0000000..b586ad4
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
@@ -0,0 +1,31 @@
+/*
+ * 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.security.keystore;
+
+import java.security.PrivateKey;
+
+/**
+ * {@link PrivateKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
+
+    public AndroidKeyStorePrivateKey(String alias, String algorithm) {
+        super(alias, algorithm);
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 649a515..de4213e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -20,6 +20,7 @@
 
 import java.security.Provider;
 import java.security.Security;
+import java.security.Signature;
 
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
@@ -118,12 +119,15 @@
             throw new NullPointerException();
         }
         Object spi;
-        if (cryptoPrimitive instanceof Mac) {
+        if (cryptoPrimitive instanceof Signature) {
+            spi = ((Signature) cryptoPrimitive).getCurrentSpi();
+        } else if (cryptoPrimitive instanceof Mac) {
             spi = ((Mac) cryptoPrimitive).getCurrentSpi();
         } else if (cryptoPrimitive instanceof Cipher) {
             spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
         } else {
-            throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
+            throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
+                    + ". Supported: Signature, Mac, Cipher");
         }
         if (spi == null) {
             throw new IllegalStateException("Crypto primitive not initialized");
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
new file mode 100644
index 0000000..8133d46
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
@@ -0,0 +1,44 @@
+/*
+ * 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.security.keystore;
+
+import java.security.PublicKey;
+
+/**
+ * {@link PublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
+
+    private final byte[] mEncoded;
+
+    public AndroidKeyStorePublicKey(String alias, String algorithm, byte[] x509EncodedForm) {
+        super(alias, algorithm);
+        mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm);
+    }
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return ArrayUtils.cloneIfNotEmpty(mEncoded);
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
new file mode 100644
index 0000000..f70c323
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -0,0 +1,487 @@
+/*
+ * 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import libcore.util.EmptyArray;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+    /**
+     * Raw RSA cipher without any padding.
+     */
+    public static final class NoPadding extends AndroidKeyStoreRSACipherSpi {
+        public NoPadding() {
+            super(KeymasterDefs.KM_PAD_NONE);
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected AlgorithmParameters engineGetParameters() {
+            return null;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return 0;
+        }
+
+        @Override
+        @NonNull
+        protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+                KeyStore keyStore, IBinder operationToken) {
+            if (isEncrypting()) {
+                // KeyStore's RSA encryption without padding expects the input to be of the same
+                // length as the modulus. We thus have to buffer all input to pad it with leading
+                // zeros.
+                return new ZeroPaddingEncryptionStreamer(
+                        super.createMainDataStreamer(keyStore, operationToken),
+                        getModulusSizeBytes());
+            } else {
+                return super.createMainDataStreamer(keyStore, operationToken);
+            }
+        }
+
+        /**
+         * Streamer which buffers all plaintext input, then pads it with leading zeros to match
+         * modulus size, and then sends it into KeyStore to obtain ciphertext.
+         */
+        private static class ZeroPaddingEncryptionStreamer
+                implements KeyStoreCryptoOperationStreamer {
+
+            private final KeyStoreCryptoOperationStreamer mDelegate;
+            private final int mModulusSizeBytes;
+            private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
+
+            private ZeroPaddingEncryptionStreamer(
+                    KeyStoreCryptoOperationStreamer delegate,
+                    int modulusSizeBytes) {
+                mDelegate = delegate;
+                mModulusSizeBytes = modulusSizeBytes;
+            }
+
+            @Override
+            public byte[] update(byte[] input, int inputOffset, int inputLength)
+                    throws KeyStoreException {
+                if (inputLength > 0) {
+                    mInputBuffer.write(input, inputOffset, inputLength);
+                }
+                return EmptyArray.BYTE;
+            }
+
+            @Override
+            public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+                    throws KeyStoreException {
+                if (inputLength > 0) {
+                    mInputBuffer.write(input, inputOffset, inputLength);
+                }
+                byte[] bufferedInput = mInputBuffer.toByteArray();
+                mInputBuffer.reset();
+                byte[] paddedInput;
+                if (bufferedInput.length < mModulusSizeBytes) {
+                    // Pad input with leading zeros
+                    paddedInput = new byte[mModulusSizeBytes];
+                    System.arraycopy(
+                            bufferedInput, 0,
+                            paddedInput,
+                            paddedInput.length - bufferedInput.length,
+                            bufferedInput.length);
+                } else {
+                    // No need to pad input
+                    paddedInput = bufferedInput;
+                }
+                return mDelegate.doFinal(paddedInput, 0, paddedInput.length);
+            }
+        }
+    }
+
+    /**
+     * RSA cipher with PKCS#1 v1.5 encryption padding.
+     */
+    public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi {
+        public PKCS1Padding() {
+            super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unexpected parameters: " + params + ". No parameters supported");
+            }
+        }
+
+        @Override
+        protected AlgorithmParameters engineGetParameters() {
+            return null;
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return (isEncrypting()) ? getModulusSizeBytes() : 0;
+        }
+    }
+
+    /**
+     * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
+     */
+    abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
+
+        private static final String MGF_ALGORITGM_MGF1 = "MGF1";
+
+        private int mKeymasterDigest = -1;
+        private int mDigestOutputSizeBytes;
+
+        OAEPWithMGF1Padding(int keymasterDigest) {
+            super(KeymasterDefs.KM_PAD_RSA_OAEP);
+            mKeymasterDigest = keymasterDigest;
+            mDigestOutputSizeBytes =
+                    (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+        @Override
+        protected final void initAlgorithmSpecificParameters(
+                @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
+            if (params == null) {
+                return;
+            }
+
+            if (!(params instanceof OAEPParameterSpec)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported parameter spec: " + params
+                        + ". Only OAEPParameterSpec supported");
+            }
+            OAEPParameterSpec spec = (OAEPParameterSpec) params;
+            if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF: " + spec.getMGFAlgorithm()
+                        + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
+            }
+            String jcaDigest = spec.getDigestAlgorithm();
+            int keymasterDigest;
+            try {
+                keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest);
+            } catch (IllegalArgumentException e) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported digest: " + jcaDigest, e);
+            }
+            switch (keymasterDigest) {
+                case KeymasterDefs.KM_DIGEST_SHA1:
+                case KeymasterDefs.KM_DIGEST_SHA_2_224:
+                case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                case KeymasterDefs.KM_DIGEST_SHA_2_384:
+                case KeymasterDefs.KM_DIGEST_SHA_2_512:
+                    // Permitted.
+                    break;
+                default:
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported digest: " + jcaDigest);
+            }
+            AlgorithmParameterSpec mgfParams = spec.getMGFParameters();
+            if (mgfParams == null) {
+                throw new InvalidAlgorithmParameterException("MGF parameters must be provided");
+            }
+            // Check whether MGF parameters match the OAEPParameterSpec
+            if (!(mgfParams instanceof MGF1ParameterSpec)) {
+                throw new InvalidAlgorithmParameterException("Unsupported MGF parameters"
+                        + ": " + mgfParams + ". Only MGF1ParameterSpec supported");
+            }
+            MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
+            String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
+            if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF1 digest: " + mgf1JcaDigest
+                        + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
+            }
+            PSource pSource = spec.getPSource();
+            if (!(pSource instanceof PSource.PSpecified)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported source of encoding input P: " + pSource
+                        + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+            }
+            PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource;
+            byte[] pSourceValue = pSourceSpecified.getValue();
+            if ((pSourceValue != null) && (pSourceValue.length > 0)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported source of encoding input P: " + pSource
+                        + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+            }
+            mKeymasterDigest = keymasterDigest;
+            mDigestOutputSizeBytes =
+                    (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+        }
+
+        @Override
+        protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+                throws InvalidAlgorithmParameterException {
+            if (params == null) {
+                return;
+            }
+
+            OAEPParameterSpec spec;
+            try {
+                spec = params.getParameterSpec(OAEPParameterSpec.class);
+            } catch (InvalidParameterSpecException e) {
+                throw new InvalidAlgorithmParameterException("OAEP parameters required"
+                        + ", but not found in parameters: " + params, e);
+            }
+            if (spec == null) {
+                throw new InvalidAlgorithmParameterException("OAEP parameters required"
+                        + ", but not provided in parameters: " + params);
+            }
+            initAlgorithmSpecificParameters(spec);
+        }
+
+        @Override
+        protected final AlgorithmParameters engineGetParameters() {
+            OAEPParameterSpec spec =
+                    new OAEPParameterSpec(
+                            KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
+                            MGF_ALGORITGM_MGF1,
+                            MGF1ParameterSpec.SHA1,
+                            PSource.PSpecified.DEFAULT);
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
+                params.init(spec);
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException(
+                        "Failed to obtain OAEP AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new ProviderException(
+                        "Failed to initialize OAEP AlgorithmParameters with an IV",
+                        e);
+            }
+        }
+
+        @Override
+        protected final void addAlgorithmSpecificParametersToBegin(
+                KeymasterArguments keymasterArgs) {
+            super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
+            keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+        }
+
+        @Override
+        protected final void loadAlgorithmSpecificParametersFromBeginResult(
+                @NonNull KeymasterArguments keymasterArgs) {
+            super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
+        }
+    }
+
+    public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA1AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA224AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA256AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA384AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding {
+        public OAEPWithSHA512AndMGF1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterPadding;
+
+    private int mModulusSizeBytes = -1;
+
+    AndroidKeyStoreRSACipherSpi(int keymasterPadding) {
+        mKeymasterPadding = keymasterPadding;
+    }
+
+    @Override
+    protected final void initKey(int opmode, Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("Unsupported key: null");
+        }
+        if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+                    + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+        }
+        AndroidKeyStoreKey keystoreKey;
+        if (key instanceof AndroidKeyStorePrivateKey) {
+            keystoreKey = (AndroidKeyStoreKey) key;
+        } else if (key instanceof AndroidKeyStorePublicKey) {
+            keystoreKey = (AndroidKeyStoreKey) key;
+        } else {
+            throw new InvalidKeyException("Unsupported key type: " + key);
+        }
+
+        if (keystoreKey instanceof PrivateKey) {
+            if ((opmode != Cipher.DECRYPT_MODE) && (opmode != Cipher.UNWRAP_MODE)) {
+                throw new InvalidKeyException("Private key cannot be used with opmode: " + opmode
+                        + ". Only DECRYPT_MODE and UNWRAP_MODE supported");
+            }
+        } else {
+            if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.WRAP_MODE)) {
+                throw new InvalidKeyException("Public key cannot be used with opmode: " + opmode
+                        + ". Only ENCRYPT_MODE and WRAP_MODE supported");
+            }
+        }
+
+        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+        int errorCode = getKeyStore().getKeyCharacteristics(
+                keystoreKey.getAlias(), null, null, keyCharacteristics);
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw getKeyStore().getInvalidKeyException(keystoreKey.getAlias(), errorCode);
+        }
+        int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+        if (keySizeBits == -1) {
+            throw new InvalidKeyException("Size of key not known");
+        }
+        mModulusSizeBytes = (keySizeBits + 7) / 8;
+
+        setKey(keystoreKey);
+    }
+
+    @Override
+    protected final void resetAll() {
+        mModulusSizeBytes = -1;
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected void addAlgorithmSpecificParametersToBegin(
+            @NonNull KeymasterArguments keymasterArgs) {
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+    }
+
+    @Override
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            @NonNull KeymasterArguments keymasterArgs) {
+    }
+
+    @Override
+    protected final int engineGetBlockSize() {
+        // Not a block cipher, according to the RI
+        return 0;
+    }
+
+    @Override
+    protected final byte[] engineGetIV() {
+        // IV never used
+        return null;
+    }
+
+    @Override
+    protected final int engineGetOutputSize(int inputLen) {
+        return getModulusSizeBytes();
+    }
+
+    protected final int getModulusSizeBytes() {
+        if (mModulusSizeBytes == -1) {
+            throw new IllegalStateException("Not initialized");
+        }
+        return mModulusSizeBytes;
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
new file mode 100644
index 0000000..08a173e
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
@@ -0,0 +1,55 @@
+/*
+ * 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.security.keystore;
+
+import java.math.BigInteger;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * {@link RSAPublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implements RSAPublicKey {
+    private final BigInteger mModulus;
+    private final BigInteger mPublicExponent;
+
+    public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
+            BigInteger publicExponent) {
+        super(alias, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
+        mModulus = modulus;
+        mPublicExponent = publicExponent;
+    }
+
+    public AndroidKeyStoreRSAPublicKey(String alias, RSAPublicKey info) {
+        this(alias, info.getEncoded(), info.getModulus(), info.getPublicExponent());
+        if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+            throw new IllegalArgumentException(
+                    "Unsupported key export format: " + info.getFormat());
+        }
+    }
+
+    @Override
+    public BigInteger getModulus() {
+        return mModulus;
+    }
+
+    @Override
+    public BigInteger getPublicExponent() {
+        return mPublicExponent;
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
new file mode 100644
index 0000000..898336d
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
@@ -0,0 +1,164 @@
+/*
+ * 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.security.keystore;
+
+import android.annotation.NonNull;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.SignatureSpi;
+
+/**
+ * Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
+
+    abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
+        PKCS1Padding(int keymasterDigest) {
+            super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            // No entropy required for this deterministic signature scheme.
+            return 0;
+        }
+    }
+
+    public static final class NONEWithPKCS1Padding extends PKCS1Padding {
+        public NONEWithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_NONE);
+        }
+    }
+
+    public static final class MD5WithPKCS1Padding extends PKCS1Padding {
+        public MD5WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_MD5);
+        }
+    }
+
+    public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
+        public SHA1WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
+        public SHA224WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
+        public SHA256WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
+        public SHA384WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
+        public SHA512WithPKCS1Padding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
+        private static final int SALT_LENGTH_BYTES = 20;
+
+        PSSPadding(int keymasterDigest) {
+            super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS);
+        }
+
+        @Override
+        protected final int getAdditionalEntropyAmountForBegin() {
+            return (isSigning()) ? SALT_LENGTH_BYTES : 0;
+        }
+    }
+
+    public static final class SHA1WithPSSPadding extends PSSPadding {
+        public SHA1WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA1);
+        }
+    }
+
+    public static final class SHA224WithPSSPadding extends PSSPadding {
+        public SHA224WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+        }
+    }
+
+    public static final class SHA256WithPSSPadding extends PSSPadding {
+        public SHA256WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+        }
+    }
+
+    public static final class SHA384WithPSSPadding extends PSSPadding {
+        public SHA384WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+        }
+    }
+
+    public static final class SHA512WithPSSPadding extends PSSPadding {
+        public SHA512WithPSSPadding() {
+            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+        }
+    }
+
+    private final int mKeymasterDigest;
+    private final int mKeymasterPadding;
+
+    AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) {
+        mKeymasterDigest = keymasterDigest;
+        mKeymasterPadding = keymasterPadding;
+    }
+
+    @Override
+    protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+        if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+                    + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+        }
+        super.initKey(key);
+    }
+
+    @Override
+    protected final void resetAll() {
+        super.resetAll();
+    }
+
+    @Override
+    protected final void resetWhilePreservingInitState() {
+        super.resetWhilePreservingInitState();
+    }
+
+    @Override
+    protected final void addAlgorithmSpecificParametersToBegin(
+            @NonNull KeymasterArguments keymasterArgs) {
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
index f75516b..af354ab 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
@@ -19,7 +19,7 @@
 import javax.crypto.SecretKey;
 
 /**
- * {@link SecretKey} backed by keystore.
+ * {@link SecretKey} backed by Android Keystore.
  *
  * @hide
  */
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
new file mode 100644
index 0000000..4c4062f
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
@@ -0,0 +1,409 @@
+/*
+ * 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.security.keystore;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import com.android.org.conscrypt.util.EmptyArray;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
+        implements KeyStoreCryptoOperation {
+    private final KeyStore mKeyStore;
+
+    // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
+    // and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
+    private boolean mSigning;
+    private AndroidKeyStoreKey mKey;
+
+    /**
+     * Token referencing this operation inside keystore service. It is initialized by
+     * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
+     * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
+     */
+    private IBinder mOperationToken;
+    private long mOperationHandle;
+    private KeyStoreCryptoOperationChunkedStreamer mMessageStreamer;
+
+    /**
+     * Encountered exception which could not be immediately thrown because it was encountered inside
+     * a method that does not throw checked exception. This exception will be thrown from
+     * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
+     * {@code engineUpdate} starts ignoring input data.
+     */
+    private Exception mCachedException;
+
+    AndroidKeyStoreSignatureSpiBase() {
+        mKeyStore = KeyStore.getInstance();
+    }
+
+    @Override
+    protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
+        engineInitSign(key, null);
+    }
+
+    @Override
+    protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
+            throws InvalidKeyException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if (privateKey == null) {
+                throw new InvalidKeyException("Unsupported key: null");
+            }
+            AndroidKeyStoreKey keystoreKey;
+            if (privateKey instanceof AndroidKeyStorePrivateKey) {
+                keystoreKey = (AndroidKeyStoreKey) privateKey;
+            } else {
+                throw new InvalidKeyException("Unsupported private key type: " + privateKey);
+            }
+            mSigning = true;
+            initKey(keystoreKey);
+            appRandom = random;
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    @Override
+    protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            if (publicKey == null) {
+                throw new InvalidKeyException("Unsupported key: null");
+            }
+            AndroidKeyStoreKey keystoreKey;
+            if (publicKey instanceof AndroidKeyStorePublicKey) {
+                keystoreKey = (AndroidKeyStorePublicKey) publicKey;
+            } else {
+                throw new InvalidKeyException("Unsupported public key type: " + publicKey);
+            }
+            mSigning = false;
+            initKey(keystoreKey);
+            appRandom = null;
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    /**
+     * Configures this signature instance to use the provided key.
+     *
+     * @throws InvalidKeyException if the {@code key} is not suitable.
+     */
+    @CallSuper
+    protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
+        mKey = key;
+    }
+
+    /**
+     * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
+     * cipher instance.
+     *
+     * <p>Subclasses storing additional state should override this method, reset the additional
+     * state, and then chain to superclass.
+     */
+    @CallSuper
+    protected void resetAll() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mSigning = false;
+        mKey = null;
+        appRandom = null;
+        mOperationToken = null;
+        mOperationHandle = 0;
+        mMessageStreamer = null;
+        mCachedException = null;
+    }
+
+    /**
+     * Resets this cipher while preserving the initialized state. This must be equivalent to
+     * rolling back the cipher's state to just after the most recent {@code engineInit} completed
+     * successfully.
+     *
+     * <p>Subclasses storing additional post-init state should override this method, reset the
+     * additional state, and then chain to superclass.
+     */
+    @CallSuper
+    protected void resetWhilePreservingInitState() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mOperationHandle = 0;
+        mMessageStreamer = null;
+        mCachedException = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
+        if (mMessageStreamer != null) {
+            return;
+        }
+        if (mCachedException != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        KeymasterArguments keymasterInputArgs = new KeymasterArguments();
+        addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
+        byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+                appRandom, getAdditionalEntropyAmountForBegin());
+
+        OperationResult opResult = mKeyStore.begin(
+                mKey.getAlias(),
+                mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
+                true, // permit aborting this operation if keystore runs out of resources
+                keymasterInputArgs,
+                additionalEntropy);
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        }
+
+        // Store operation token and handle regardless of the error code returned by KeyStore to
+        // ensure that the operation gets aborted immediately if the code below throws an exception.
+        mOperationToken = opResult.token;
+        mOperationHandle = opResult.operationHandle;
+
+        // If necessary, throw an exception due to KeyStore operation having failed.
+        InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
+                mKeyStore, mKey, opResult.resultCode);
+        if (e != null) {
+            throw e;
+        }
+
+        if (mOperationToken == null) {
+            throw new ProviderException("Keystore returned null operation token");
+        }
+        if (mOperationHandle == 0) {
+            throw new ProviderException("Keystore returned invalid operation handle");
+        }
+
+        mMessageStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, opResult.token));
+    }
+
+    @Override
+    public final long getOperationHandle() {
+        return mOperationHandle;
+    }
+
+    @Override
+    protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+        if (mCachedException != null) {
+            throw new SignatureException(mCachedException);
+        }
+
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new SignatureException(e);
+        }
+
+        if (len == 0) {
+            return;
+        }
+
+        byte[] output;
+        try {
+            output = mMessageStreamer.update(b, off, len);
+        } catch (KeyStoreException e) {
+            throw new SignatureException(e);
+        }
+
+        if (output.length != 0) {
+            throw new ProviderException(
+                    "Update operation unexpectedly produced output: " + output.length + " bytes");
+        }
+    }
+
+    @Override
+    protected final void engineUpdate(byte b) throws SignatureException {
+        engineUpdate(new byte[] {b}, 0, 1);
+    }
+
+    @Override
+    protected final void engineUpdate(ByteBuffer input) {
+        byte[] b;
+        int off;
+        int len = input.remaining();
+        if (input.hasArray()) {
+            b = input.array();
+            off = input.arrayOffset() + input.position();
+            input.position(input.limit());
+        } else {
+            b = new byte[len];
+            off = 0;
+            input.get(b);
+        }
+
+        try {
+            engineUpdate(b, off, len);
+        } catch (SignatureException e) {
+            mCachedException = e;
+        }
+    }
+
+    @Override
+    protected final int engineSign(byte[] out, int outOffset, int outLen)
+            throws SignatureException {
+        return super.engineSign(out, outOffset, outLen);
+    }
+
+    @Override
+    protected final byte[] engineSign() throws SignatureException {
+        if (mCachedException != null) {
+            throw new SignatureException(mCachedException);
+        }
+
+        byte[] signature;
+        try {
+            ensureKeystoreOperationInitialized();
+            signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0);
+        } catch (InvalidKeyException | KeyStoreException e) {
+            throw new SignatureException(e);
+        }
+
+        resetWhilePreservingInitState();
+        return signature;
+    }
+
+    @Override
+    protected final boolean engineVerify(byte[] signature) throws SignatureException {
+        if (mCachedException != null) {
+            throw new SignatureException(mCachedException);
+        }
+
+        boolean result;
+        try {
+            ensureKeystoreOperationInitialized();
+            mMessageStreamer.flush();
+            OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature);
+            if (opResult == null) {
+                throw new KeyStoreConnectException();
+            }
+            switch (opResult.resultCode) {
+                case KeyStore.NO_ERROR:
+                    result = true;
+                    break;
+                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+                    result = false;
+                    break;
+                default:
+                    throw new SignatureException(
+                            KeyStore.getKeyStoreException(opResult.resultCode));
+            }
+        } catch (InvalidKeyException | KeyStoreException e) {
+            throw new SignatureException(e);
+        }
+
+        resetWhilePreservingInitState();
+        return result;
+    }
+
+    @Override
+    protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
+            throws SignatureException {
+        return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
+    }
+
+    @Deprecated
+    @Override
+    protected final Object engineGetParameter(String param) throws InvalidParameterException {
+        throw new InvalidParameterException();
+    }
+
+    @Deprecated
+    @Override
+    protected final void engineSetParameter(String param, Object value)
+            throws InvalidParameterException {
+        throw new InvalidParameterException();
+    }
+
+    protected final KeyStore getKeyStore() {
+        return mKeyStore;
+    }
+
+    /**
+     * Returns {@code true} if this signature is initialized for signing, {@code false} if this
+     * signature is initialized for verification.
+     */
+    protected final boolean isSigning() {
+        return mSigning;
+    }
+
+    // The methods below need to be implemented by subclasses.
+
+    /**
+     * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
+     * {@code begin} operation.
+     *
+     * <p>For signature verification, this should be {@code 0} because verification should not be
+     * consuming any entropy. For signature generation, this value should match (or exceed) the
+     * amount of Shannon entropy of the produced signature assuming the key and the message are
+     * known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the
+     * RSA signature with PKCS#1 padding this should be {@code 0}.
+     */
+    protected abstract int getAdditionalEntropyAmountForBegin();
+
+    /**
+     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+     *
+     * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+     *        parameters.
+     */
+    protected abstract void addAlgorithmSpecificParametersToBegin(
+            @NonNull KeymasterArguments keymasterArgs);
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 701bd67..05ddef6 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -674,12 +674,13 @@
 
     @Override
     public void engineDeleteEntry(String alias) throws KeyStoreException {
-        if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
+        if (!engineContainsAlias(alias)) {
             return;
         }
+        // At least one entry corresponding to this alias exists in keystore
 
         if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
-            throw new KeyStoreException("No such entry " + alias);
+            throw new KeyStoreException("Failed to delete entry: " + alias);
         }
     }
 
@@ -849,9 +850,7 @@
             throw new KeyStoreException("entry == null");
         }
 
-        if (engineContainsAlias(alias)) {
-            engineDeleteEntry(alias);
-        }
+        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
 
         if (entry instanceof KeyStore.TrustedCertificateEntry) {
             KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 7d57e5f..47b4996 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -44,12 +44,12 @@
  *
  * @hide
  */
-public class KeyStoreCryptoOperationChunkedStreamer {
+class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
 
     /**
      * Bidirectional chunked data stream over a KeyStore crypto operation.
      */
-    public interface Stream {
+    interface Stream {
         /**
          * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
          * be reached.
@@ -66,12 +66,11 @@
     // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
     // Thus, it's safer to use a much smaller upper bound.
     private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
-    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
     private final Stream mKeyStoreStream;
     private final int mMaxChunkSize;
 
-    private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+    private byte[] mBuffered = EmptyArray.BYTE;
     private int mBufferedOffset;
     private int mBufferedLength;
 
@@ -84,10 +83,11 @@
         mMaxChunkSize = maxChunkSize;
     }
 
+    @Override
     public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
         if (inputLength == 0) {
             // No input provided
-            return EMPTY_BYTE_ARRAY;
+            return EmptyArray.BYTE;
         }
 
         ByteArrayOutputStream bufferedOutput = null;
@@ -129,7 +129,7 @@
 
             if (opResult.inputConsumed == chunk.length) {
                 // The whole chunk was consumed
-                mBuffered = EMPTY_BYTE_ARRAY;
+                mBuffered = EmptyArray.BYTE;
                 mBufferedOffset = 0;
                 mBufferedLength = 0;
             } else if (opResult.inputConsumed == 0) {
@@ -185,17 +185,18 @@
 
         if (bufferedOutput == null) {
             // No output produced
-            return EMPTY_BYTE_ARRAY;
+            return EmptyArray.BYTE;
         } else {
             return bufferedOutput.toByteArray();
         }
     }
 
+    @Override
     public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
             throws KeyStoreException {
         if (inputLength == 0) {
             // No input provided -- simplify the rest of the code
-            input = EMPTY_BYTE_ARRAY;
+            input = EmptyArray.BYTE;
             inputOffset = 0;
         }
 
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
new file mode 100644
index 0000000..2fb8f20
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.security.keystore;
+
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@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 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.
+ *
+ * @hide
+ */
+interface KeyStoreCryptoOperationStreamer {
+    byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+    byte[] doFinal(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+}
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 38a6a21..6815254 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -30,35 +30,36 @@
 #define UI_THREAD_FRAME_INFO_SIZE 9
 
 enum class FrameInfoIndex {
-    kFlags = 0,
-    kIntendedVsync,
-    kVsync,
-    kOldestInputEvent,
-    kNewestInputEvent,
-    kHandleInputStart,
-    kAnimationStart,
-    kPerformTraversalsStart,
-    kDrawStart,
+    Flags = 0,
+    IntendedVsync,
+    Vsync,
+    OldestInputEvent,
+    NewestInputEvent,
+    HandleInputStart,
+    AnimationStart,
+    PerformTraversalsStart,
+    DrawStart,
     // End of UI frame info
 
-    kSyncStart,
-    kIssueDrawCommandsStart,
-    kSwapBuffers,
-    kFrameCompleted,
+    SyncStart,
+    IssueDrawCommandsStart,
+    SwapBuffers,
+    FrameCompleted,
 
     // Must be the last value!
-    kNumIndexes
+    NumIndexes
 };
 
 extern const std::string FrameInfoNames[];
 
-enum class FrameInfoFlags {
-    kWindowLayoutChanged = 1 << 0,
-    kRTAnimation = 1 << 1,
-    kSurfaceCanvas = 1 << 2,
-    kSkippedFrame = 1 << 3,
+namespace FrameInfoFlags {
+    enum {
+        WindowLayoutChanged = 1 << 0,
+        RTAnimation = 1 << 1,
+        SurfaceCanvas = 1 << 2,
+        SkippedFrame = 1 << 3,
+    };
 };
-MAKE_FLAGS_ENUM(FrameInfoFlags)
 
 class ANDROID_API UiFrameInfoBuilder {
 public:
@@ -67,19 +68,19 @@
     }
 
     UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
-        set(FrameInfoIndex::kVsync) = vsyncTime;
-        set(FrameInfoIndex::kIntendedVsync) = intendedVsync;
+        set(FrameInfoIndex::Vsync) = vsyncTime;
+        set(FrameInfoIndex::IntendedVsync) = intendedVsync;
         // Pretend the other fields are all at vsync, too, so that naive
         // duration calculations end up being 0 instead of very large
-        set(FrameInfoIndex::kHandleInputStart) = vsyncTime;
-        set(FrameInfoIndex::kAnimationStart) = vsyncTime;
-        set(FrameInfoIndex::kPerformTraversalsStart) = vsyncTime;
-        set(FrameInfoIndex::kDrawStart) = vsyncTime;
+        set(FrameInfoIndex::HandleInputStart) = vsyncTime;
+        set(FrameInfoIndex::AnimationStart) = vsyncTime;
+        set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
+        set(FrameInfoIndex::DrawStart) = vsyncTime;
         return *this;
     }
 
-    UiFrameInfoBuilder& addFlag(FrameInfoFlags flag) {
-        set(FrameInfoIndex::kFlags) |= static_cast<uint64_t>(flag);
+    UiFrameInfoBuilder& addFlag(int frameInfoFlag) {
+        set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
         return *this;
     }
 
@@ -96,32 +97,32 @@
     void importUiThreadInfo(int64_t* info);
 
     void markSyncStart() {
-        set(FrameInfoIndex::kSyncStart) = systemTime(CLOCK_MONOTONIC);
+        set(FrameInfoIndex::SyncStart) = systemTime(CLOCK_MONOTONIC);
     }
 
     void markIssueDrawCommandsStart() {
-        set(FrameInfoIndex::kIssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC);
+        set(FrameInfoIndex::IssueDrawCommandsStart) = systemTime(CLOCK_MONOTONIC);
     }
 
     void markSwapBuffers() {
-        set(FrameInfoIndex::kSwapBuffers) = systemTime(CLOCK_MONOTONIC);
+        set(FrameInfoIndex::SwapBuffers) = systemTime(CLOCK_MONOTONIC);
     }
 
     void markFrameCompleted() {
-        set(FrameInfoIndex::kFrameCompleted) = systemTime(CLOCK_MONOTONIC);
+        set(FrameInfoIndex::FrameCompleted) = systemTime(CLOCK_MONOTONIC);
     }
 
-    void addFlag(FrameInfoFlags flag) {
-        set(FrameInfoIndex::kFlags) |= static_cast<uint64_t>(flag);
+    void addFlag(int frameInfoFlag) {
+        set(FrameInfoIndex::Flags) |= static_cast<uint64_t>(frameInfoFlag);
     }
 
     int64_t operator[](FrameInfoIndex index) const {
-        if (index == FrameInfoIndex::kNumIndexes) return 0;
+        if (index == FrameInfoIndex::NumIndexes) return 0;
         return mFrameInfo[static_cast<int>(index)];
     }
 
     int64_t operator[](int index) const {
-        if (index < 0 || index >= static_cast<int>(FrameInfoIndex::kNumIndexes)) return 0;
+        if (index < 0 || index >= static_cast<int>(FrameInfoIndex::NumIndexes)) return 0;
         return mFrameInfo[index];
     }
 
@@ -130,7 +131,7 @@
         return mFrameInfo[static_cast<int>(index)];
     }
 
-    int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::kNumIndexes)];
+    int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)];
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 95b0995..9557cb0 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -46,15 +46,15 @@
 };
 
 static const std::array<BarSegment,9> Bar {{
-    { FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync, 0x00695C },
-    { FrameInfoIndex::kVsync, FrameInfoIndex::kHandleInputStart, 0x00796B },
-    { FrameInfoIndex::kHandleInputStart, FrameInfoIndex::kAnimationStart, 0x00897B },
-    { FrameInfoIndex::kAnimationStart, FrameInfoIndex::kPerformTraversalsStart, 0x009688 },
-    { FrameInfoIndex::kPerformTraversalsStart, FrameInfoIndex::kDrawStart, 0x26A69A},
-    { FrameInfoIndex::kDrawStart, FrameInfoIndex::kSyncStart, 0x2196F3},
-    { FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart, 0x4FC3F7},
-    { FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kSwapBuffers, 0xF44336},
-    { FrameInfoIndex::kSwapBuffers, FrameInfoIndex::kFrameCompleted, 0xFF9800},
+    { FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync, 0x00695C },
+    { FrameInfoIndex::Vsync, FrameInfoIndex::HandleInputStart, 0x00796B },
+    { FrameInfoIndex::HandleInputStart, FrameInfoIndex::AnimationStart, 0x00897B },
+    { FrameInfoIndex::AnimationStart, FrameInfoIndex::PerformTraversalsStart, 0x009688 },
+    { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x26A69A},
+    { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
+    { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
+    { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
+    { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800},
 }};
 
 static int dpToPx(int dp, float density) {
@@ -137,7 +137,7 @@
     for (size_t fi = 0, ri = 0; fi < mFrameSource.size(); fi++, ri += 4) {
         // TODO: Skipped frames will leave little holes in the graph, but this
         // is better than bogus and freaky lines, so...
-        if (mFrameSource[fi][FrameInfoIndex::kFlags] & FrameInfoFlags::kSkippedFrame) {
+        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
             continue;
         }
 
@@ -166,7 +166,7 @@
     size_t fi = mFrameSource.size() - 1;
     size_t ri = fi * 4;
     float top = baseline - (mVerticalUnit * duration(fi,
-            FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kIssueDrawCommandsStart));
+            FrameInfoIndex::IntendedVsync, FrameInfoIndex::IssueDrawCommandsStart));
     canvas->drawRect(mRects[ri], top, mRects[ri + 2], baseline, &paint);
 }
 
@@ -214,15 +214,15 @@
     fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
 
     for (size_t i = 0; i < mFrameSource.size(); i++) {
-        if (mFrameSource[i][FrameInfoIndex::kIntendedVsync] <= mLastFrameLogged) {
+        if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
             continue;
         }
-        mLastFrameLogged = mFrameSource[i][FrameInfoIndex::kIntendedVsync];
+        mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
         fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
-                duration(i, FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kSyncStart),
-                duration(i, FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart),
-                duration(i, FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kSwapBuffers),
-                duration(i, FrameInfoIndex::kSwapBuffers, FrameInfoIndex::kFrameCompleted));
+                duration(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
+                duration(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
+                duration(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
+                duration(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
     }
 
     fflush(file);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index cc5c403..5c0801e 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -40,11 +40,11 @@
 };
 
 static const Comparison COMPARISONS[] = {
-        {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
-        {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
-        {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
-        {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
-        {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
+        {FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
+        {FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
+        {FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
+        {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
+        {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
 };
 
 // If the event exceeds 10 seconds throw it away, this isn't a jank event
@@ -64,8 +64,8 @@
  * time on the RenderThread, figure out how to attribute that as a jank-causer
  */
 static const int64_t EXEMPT_FRAMES_FLAGS
-        = FrameInfoFlags::kWindowLayoutChanged
-        | FrameInfoFlags::kSurfaceCanvas;
+        = FrameInfoFlags::WindowLayoutChanged
+        | FrameInfoFlags::SurfaceCanvas;
 
 // The bucketing algorithm controls so to speak
 // If a frame is <= to this it goes in bucket 0
@@ -206,7 +206,7 @@
     mData->totalFrameCount++;
     // Fast-path for jank-free frames
     int64_t totalDuration =
-            frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
+            frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync];
     uint32_t framebucket = frameCountIndexForFrameTime(
             totalDuration, mData->frameCounts.size());
     // Keep the fast path as fast as possible.
@@ -215,7 +215,7 @@
         return;
     }
 
-    if (frame[FrameInfoIndex::kFlags] & EXEMPT_FRAMES_FLAGS) {
+    if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
         return;
     }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index acf9c13..b88f30e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -157,7 +157,7 @@
 }
 
 static bool wasSkipped(FrameInfo* info) {
-    return info && ((*info)[FrameInfoIndex::kFlags] & FrameInfoFlags::kSkippedFrame);
+    return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
 }
 
 void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo) {
@@ -185,7 +185,7 @@
     }
 
     if (CC_UNLIKELY(!mNativeWindow.get())) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
+        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         info.out.canDrawThisFrame = false;
         return;
     }
@@ -199,7 +199,7 @@
     info.out.canDrawThisFrame = !runningBehind;
 
     if (!info.out.canDrawThisFrame) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
+        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
     }
 
     if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
@@ -228,7 +228,7 @@
     mDamageAccumulator.finish(&dirty);
 
     if (dirty.isEmpty() && Properties::skipEmptyFrames) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
+        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         return;
     }
 
@@ -288,7 +288,7 @@
 
     int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
     UiFrameInfoBuilder(frameInfo)
-        .addFlag(FrameInfoFlags::kRTAnimation)
+        .addFlag(FrameInfoFlags::RTAnimation)
         .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
                 mRenderThread.timeLord().latestVsync());
 
@@ -400,17 +400,17 @@
 void CanvasContext::dumpFrames(int fd) {
     FILE* file = fdopen(fd, "a");
     fprintf(file, "\n\n---PROFILEDATA---\n");
-    for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::kNumIndexes); i++) {
+    for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
         fprintf(file, "%s", FrameInfoNames[i].c_str());
         fprintf(file, ",");
     }
     for (size_t i = 0; i < mFrames.size(); i++) {
         FrameInfo& frame = mFrames[i];
-        if (frame[FrameInfoIndex::kSyncStart] == 0) {
+        if (frame[FrameInfoIndex::SyncStart] == 0) {
             continue;
         }
         fprintf(file, "\n");
-        for (int i = 0; i < static_cast<int>(FrameInfoIndex::kNumIndexes); i++) {
+        for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
             fprintf(file, "%" PRId64 ",", frame[i]);
         }
     }
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 83af4ae..008e297 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -110,7 +110,7 @@
 
 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
     ATRACE_CALL();
-    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::kVsync)];
+    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     mRenderThread->timeLord().vsyncReceived(vsync);
     mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse();
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 1b31059..5ca9083 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -35,30 +35,4 @@
         static_assert(std::is_standard_layout<Type>::value, \
         #Type " must have standard layout")
 
-#define MAKE_FLAGS_ENUM(enumType) \
-        inline void operator|=(int& lhs, enumType rhs) { \
-            lhs |= static_cast<int>(rhs); \
-        } \
-        inline int operator|(int lhs, enumType rhs) { \
-            return lhs | static_cast<int>(rhs); \
-        } \
-        inline int operator|(enumType lhs, int rhs) { \
-            return static_cast<int>(lhs) | rhs; \
-        } \
-        inline int operator|(enumType lhs, enumType rhs) { \
-            return static_cast<int>(lhs) | static_cast<int>(rhs); \
-        } \
-        inline void operator&=(int& lhs, enumType rhs) { \
-            lhs &= static_cast<int>(rhs); \
-        } \
-        inline int operator&(int lhs, enumType rhs) { \
-            return lhs & static_cast<int>(rhs); \
-        } \
-        inline int operator&(enumType lhs, int rhs) { \
-            return static_cast<int>(lhs) & rhs; \
-        } \
-        inline int operator&(enumType lhs, enumType rhs) { \
-            return static_cast<int>(lhs) & static_cast<int>(rhs); \
-        }
-
 #endif /* MACROS_H */
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index e3680e9..8403c1c 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -1044,42 +1044,26 @@
 }
 
 /**
+ * Mutable version of BackgroundSpan to facilitate text rendering with edge
+ * styles.
+ *
  * @hide
- *
- * MutableBackgroundColorSpan
- *
- * This is a mutable version of BackgroundSpan to facilitate text
- * rendering with edge styles.
- *
  */
-class MutableBackgroundColorSpan extends CharacterStyle
-        implements UpdateAppearance, ParcelableSpan {
+class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance {
     private int mColor;
 
     public MutableBackgroundColorSpan(int color) {
         mColor = color;
     }
-    public MutableBackgroundColorSpan(Parcel src) {
-        mColor = src.readInt();
-    }
+
     public void setBackgroundColor(int color) {
         mColor = color;
     }
+
     public int getBackgroundColor() {
         return mColor;
     }
-    @Override
-    public int getSpanTypeId() {
-        return TextUtils.BACKGROUND_COLOR_SPAN;
-    }
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mColor);
-    }
+
     @Override
     public void updateDrawState(TextPaint ds) {
         ds.bgColor = mColor;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 8441541..166ff38 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -21,6 +21,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
+import android.media.MediaPlayer.OnCompletionListener;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -29,6 +30,7 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
 /**
  * Ringtone provides a quick method for playing a ringtone, notification, or
@@ -49,6 +51,9 @@
         MediaStore.Audio.Media.TITLE
     };
 
+    // keep references on active Ringtones until stopped or completion listener called.
+    private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
+
     private final Context mContext;
     private final AudioManager mAudioManager;
 
@@ -62,6 +67,7 @@
     private final Binder mRemoteToken;
 
     private MediaPlayer mLocalPlayer;
+    private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
 
     private Uri mUri;
     private String mTitle;
@@ -247,7 +253,7 @@
             // (typically because ringer mode is silent).
             if (mAudioManager.getStreamVolume(
                     AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
-                mLocalPlayer.start();
+                startLocalPlayer();
             }
         } else if (mAllowRemote && (mRemotePlayer != null)) {
             final Uri canonicalUri = mUri.getCanonicalUri();
@@ -285,9 +291,23 @@
             mLocalPlayer.reset();
             mLocalPlayer.release();
             mLocalPlayer = null;
+            synchronized (sActiveRingtones) {
+                sActiveRingtones.remove(this);
+            }
         }
     }
 
+    private void startLocalPlayer() {
+        if (mLocalPlayer == null) {
+            return;
+        }
+        synchronized (sActiveRingtones) {
+            sActiveRingtones.add(this);
+        }
+        mLocalPlayer.setOnCompletionListener(mCompletionListener);
+        mLocalPlayer.start();
+    }
+
     /**
      * Whether this ringtone is currently playing.
      * 
@@ -330,7 +350,7 @@
                         }
                         mLocalPlayer.setAudioAttributes(mAudioAttributes);
                         mLocalPlayer.prepare();
-                        mLocalPlayer.start();
+                        startLocalPlayer();
                         afd.close();
                         return true;
                     } else {
@@ -352,4 +372,20 @@
     void setTitle(String title) {
         mTitle = title;
     }
+
+    @Override
+    protected void finalize() {
+        if (mLocalPlayer != null) {
+            mLocalPlayer.release();
+        }
+    }
+
+    class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
+        public void onCompletion(MediaPlayer mp)
+        {
+            synchronized (sActiveRingtones) {
+                sActiveRingtones.remove(Ringtone.this);
+            }
+        }
+    }
 }
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index a9accb02..d7968d2 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -301,17 +301,19 @@
 
     status_t err = crypto->setMediaDrmSession(sessionId);
 
-    String8 msg("setMediaDrmSession failed");
-    if (err == ERROR_DRM_SESSION_NOT_OPENED) {
-        msg += ": session not opened";
-    } else if (err == ERROR_UNSUPPORTED) {
-        msg += ": not supported by this crypto scheme";
-    } else if (err == NO_INIT) {
-        msg += ": crypto plugin not initialized";
-    } else if (err != OK) {
-        msg.appendFormat(": general failure (%d)", err);
+    if (err != OK) {
+        String8 msg("setMediaDrmSession failed");
+        if (err == ERROR_DRM_SESSION_NOT_OPENED) {
+            msg += ": session not opened";
+        } else if (err == ERROR_UNSUPPORTED) {
+            msg += ": not supported by this crypto scheme";
+        } else if (err == NO_INIT) {
+            msg += ": crypto plugin not initialized";
+        } else {
+            msg.appendFormat(": general failure (%d)", err);
+        }
+        jniThrowException(env, "android/media/MediaCryptoException", msg.string());
     }
-    jniThrowException(env, "android/media/MediaCryptoException", msg.string());
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 29c3c75..cd0e587 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -731,27 +731,31 @@
 
     @SmallTest
     public void testReadWriteHighSpeedVideoConfiguration() {
-        // int32 x 4 x 1
+        // int32 x 5 x 1
         checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
                 new HighSpeedVideoConfiguration(
-                        /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200),
+                        /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200,
+                        /*batchSizeMax*/8),
                 /* width, height, fpsMin, fpsMax */
-                toByteArray(1000, 255, 30, 200));
+                toByteArray(1000, 255, 30, 200, 8));
 
-        // int32 x 4 x 3
+        // int32 x 5 x 3
         checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
                 new HighSpeedVideoConfiguration[] {
                     new HighSpeedVideoConfiguration(
-                            /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120),
+                            /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120,
+                            /*batchSizeMax*/8),
                     new HighSpeedVideoConfiguration(
-                            /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200),
+                            /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200,
+                            /*batchSizeMax*/4),
                     new HighSpeedVideoConfiguration(
-                            /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60)
+                            /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60,
+                            /*batchSizeMax*/2)
                 },
                 toByteArray(
-                        1280, 720, 60, 120,
-                        123, 456, 1, 200,
-                        4096, 2592, 30, 60
+                        1280, 720, 60, 120, 8,
+                        123, 456, 1, 200, 4,
+                        4096, 2592, 30, 60, 2
         ));
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index d3e7104..9a2f71c 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -348,8 +348,12 @@
                 Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
                 return;
             }
-            int errorMsg = R.string.bluetooth_pairing_error_message;
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+            if (cachedDevice == null) {
+                Log.e(TAG, "ACTION_PAIRING_CANCEL with no cached device");
+                return;
+            }
+            int errorMsg = R.string.bluetooth_pairing_error_message;
             Utils.showError(context, cachedDevice.getName(), errorMsg);
         }
     }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fd0ba73..640fb29 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <!-- Standard permissions granted to the shell. -->
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.READ_CALENDAR" />
@@ -97,6 +98,10 @@
     <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
+    <uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
+    <uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
 
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 53047a3..477d9d7 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -21,6 +21,7 @@
     android:layout_gravity="top|center_horizontal">
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/application_icon"
+        android:contentDescription="@string/recents_app_info_button_label"
         android:layout_width="@dimen/recents_task_view_application_icon_size"
         android:layout_height="@dimen/recents_task_view_application_icon_size"
         android:layout_marginStart="8dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 18a19cb..9f38185 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -336,7 +336,7 @@
     <dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
 
     <!-- The size of the touch targets on the keyguard for the affordances. -->
-    <dimen name="keyguard_affordance_touch_target_size">96dp</dimen>
+    <dimen name="keyguard_affordance_touch_target_size">120dp</dimen>
 
     <!-- The grow amount for the camera and phone circles when hinting -->
     <dimen name="hint_grow_amount_sideways">60dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 0369ab5..5d74604 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -74,7 +74,11 @@
         state.visible = mFlashlightController.isAvailable();
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         if (arg instanceof UserBoolean) {
-            state.value = ((UserBoolean) arg).value;
+            boolean value = ((UserBoolean) arg).value;
+            if (value == state.value) {
+                return;
+            }
+            state.value = value;
         }
         final AnimationIcon icon = state.value ? mEnable : mDisable;
         icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index c702673..bbd3e60 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -35,6 +35,7 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.MutableBoolean;
 import android.util.Pair;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -57,7 +58,6 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Annotation for a method that is only called from the primary user's SystemUI process and will be
@@ -362,7 +362,12 @@
         // RecentsActivity)
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         sInstanceLoadPlan = loader.createLoadPlan(mContext);
-        sInstanceLoadPlan.preloadRawTasks(true);
+
+        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+        MutableBoolean isTopTaskHome = new MutableBoolean(true);
+        if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+            sInstanceLoadPlan.preloadRawTasks(isTopTaskHome.value);
+        }
     }
 
     @Override
@@ -546,7 +551,7 @@
         // If Recents is the front most activity, then we should just communicate with it directly
         // to launch the first task or dismiss itself
         ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
-        AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
+        MutableBoolean isTopTaskHome = new MutableBoolean(true);
         if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
             // Notify recents to toggle itself
             Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
@@ -555,7 +560,7 @@
             return;
         } else {
             // Otherwise, start the recents activity
-            startRecentsActivity(topTask, isTopTaskHome.get());
+            startRecentsActivity(topTask, isTopTaskHome.value);
         }
     }
 
@@ -563,9 +568,9 @@
     void startRecentsActivity() {
         // Check if the top task is in the home stack, and start the recents activity
         ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
-        AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
+        MutableBoolean isTopTaskHome = new MutableBoolean(true);
         if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
-            startRecentsActivity(topTask, isTopTaskHome.get());
+            startRecentsActivity(topTask, isTopTaskHome.value);
         }
     }
 
@@ -654,6 +659,7 @@
         if (task == null) {
             // If no task is specified or we can not find the task just use the front most one
             task = tasks.get(tasks.size() - 1);
+            runningTaskOut.copyFrom(task);
         }
 
         // Get the transform for the running task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ca0f357..272d39a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -54,6 +54,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.MutableBoolean;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
@@ -67,12 +68,9 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Acts as a shim around the real system services that we need to access data from, and provides
@@ -192,7 +190,7 @@
 
         // Break early if we can't get a valid set of tasks
         if (tasks == null) {
-            return new ArrayList<ActivityManager.RecentTaskInfo>();
+            return new ArrayList<>();
         }
 
         boolean isFirstValidTask = true;
@@ -235,7 +233,7 @@
 
     /** Returns whether the recents is currently running */
     public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
-            AtomicBoolean isHomeTopMost) {
+            MutableBoolean isHomeTopMost) {
         if (topTask != null) {
             ComponentName topActivity = topTask.topActivity;
 
@@ -243,13 +241,13 @@
             if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
                     topActivity.getClassName().equals(Recents.sRecentsActivity)) {
                 if (isHomeTopMost != null) {
-                    isHomeTopMost.set(false);
+                    isHomeTopMost.value = false;
                 }
                 return true;
             }
 
             if (isHomeTopMost != null) {
-                isHomeTopMost.set(isInHomeStack(topTask.id));
+                isHomeTopMost.value = isInHomeStack(topTask.id);
             }
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 40cd211..f40c58d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -104,13 +104,9 @@
         if (mRawTasks == null) {
             preloadRawTasks(isTopTaskHome);
         }
-        int firstStackId = -1;
         int taskCount = mRawTasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-            if (firstStackId < 0) {
-                firstStackId = t.stackId;
-            }
 
             // Compose the task key
             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
@@ -158,17 +154,17 @@
 
             if (!mConfig.multiStackEnabled ||
                     Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
-                firstStackId = 0;
+                int firstStackId = 0;
                 ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
                 if (stackTasks == null) {
-                    stackTasks = new ArrayList<Task>();
+                    stackTasks = new ArrayList<>();
                     stacksTasks.put(firstStackId, stackTasks);
                 }
                 stackTasks.add(task);
             } else {
                 ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
                 if (stackTasks == null) {
-                    stackTasks = new ArrayList<Task>();
+                    stackTasks = new ArrayList<>();
                     stacksTasks.put(t.stackId, stackTasks);
                 }
                 stackTasks.add(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b3e6221..cec613c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -550,7 +550,7 @@
         if (tv == null) {
             launchRunnable.run();
         } else {
-            if (!task.group.isFrontMostTask(task)) {
+            if (task.group != null && !task.group.isFrontMostTask(task)) {
                 // For affiliated tasks that are behind other tasks, we must animate the front cards
                 // out of view before starting the task transition
                 stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 01ed08a..4a5fef3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.*;
 import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityManager;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.view.animation.AccelerateInterpolator;
@@ -672,7 +673,11 @@
             mThumbnailView.rebindToTask(mTask);
             mHeaderView.rebindToTask(mTask);
             // Rebind any listeners
-            mHeaderView.mApplicationIcon.setOnClickListener(this);
+            AccessibilityManager am = (AccessibilityManager) getContext().
+                    getSystemService(Context.ACCESSIBILITY_SERVICE);
+            if (Constants.DebugFlags.App.EnableTaskFiltering || (am != null && am.isEnabled())) {
+                mHeaderView.mApplicationIcon.setOnClickListener(this);
+            }
             mHeaderView.mDismissButton.setOnClickListener(this);
             if (mConfig.multiStackEnabled) {
                 mHeaderView.mMoveTaskButton.setOnClickListener(this);
@@ -718,9 +723,19 @@
             postDelayed(new Runnable() {
                 @Override
                 public void run() {
-                    if (Constants.DebugFlags.App.EnableTaskFiltering && v == mHeaderView.mApplicationIcon) {
-                        if (mCb != null) {
-                            mCb.onTaskViewAppIconClicked(tv);
+                    if (v == mHeaderView.mApplicationIcon) {
+                        if (Constants.DebugFlags.App.EnableTaskFiltering) {
+                            if (mCb != null) {
+                                mCb.onTaskViewAppIconClicked(tv);
+                            }
+                        } else {
+                            AccessibilityManager am = (AccessibilityManager) getContext().
+                                    getSystemService(Context.ACCESSIBILITY_SERVICE);
+                            if (am != null && am.isEnabled()) {
+                                if (mCb != null) {
+                                    mCb.onTaskViewAppInfoClicked(tv);
+                                }
+                            }
                         }
                     } else if (v == mHeaderView.mDismissButton) {
                         dismissTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index f397bc3..82f0f31 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -190,7 +190,6 @@
         } else if (t.applicationIcon != null) {
             mApplicationIcon.setImageDrawable(t.applicationIcon);
         }
-        mApplicationIcon.setContentDescription(t.contentDescription);
         if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
             mActivityDescription.setText(t.activityLabel);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 9ccff72..374d970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -23,11 +23,14 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.DisplayListCanvas;
+import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.animation.AnimationUtils;
@@ -35,6 +38,7 @@
 import android.widget.ImageView;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 /**
  * An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -77,6 +81,14 @@
     private float mMaxCircleSize;
     private Animator mPreviewClipper;
     private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
+    private boolean mSupportHardware;
+    private boolean mFinishing;
+
+    private CanvasProperty<Float> mHwCircleRadius;
+    private CanvasProperty<Float> mHwCenterX;
+    private CanvasProperty<Float> mHwCenterY;
+    private CanvasProperty<Paint> mHwCirclePaint;
+
     private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -155,6 +167,7 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
+        mSupportHardware = canvas.isHardwareAccelerated();
         drawBackgroundCircle(canvas);
         drawArrow(canvas);
         canvas.save();
@@ -196,8 +209,14 @@
 
     private void drawBackgroundCircle(Canvas canvas) {
         if (mCircleRadius > 0) {
-            updateCircleColor();
-            canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+            if (mFinishing && mSupportHardware) {
+                DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+                displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
+                        mHwCirclePaint);
+            } else {
+                updateCircleColor();
+                canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+            }
         }
     }
 
@@ -218,15 +237,23 @@
     public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
         cancelAnimator(mCircleAnimator);
         cancelAnimator(mPreviewClipper);
+        mFinishing = true;
         mCircleStartRadius = mCircleRadius;
         float maxCircleSize = getMaxCircleSize();
-        ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
+        Animator animatorToRadius;
+        if (mSupportHardware) {
+            initHwProperties();
+            animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+        } else {
+            animatorToRadius = getAnimatorToRadius(maxCircleSize);
+        }
         mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
                 velocity, maxCircleSize);
         animatorToRadius.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mAnimationEndRunnable.run();
+                mFinishing = false;
             }
         });
         animatorToRadius.start();
@@ -240,9 +267,34 @@
                     velocity, maxCircleSize);
             mPreviewClipper.addListener(mClipEndListener);
             mPreviewClipper.start();
+            if (mSupportHardware) {
+                startRtCircleFadeOut(animatorToRadius.getDuration());
+            }
         }
     }
 
+    private void startRtCircleFadeOut(long duration) {
+        RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+                RenderNodeAnimator.PAINT_ALPHA, 0);
+        animator.setDuration(duration);
+        animator.setInterpolator(PhoneStatusBar.ALPHA_OUT);
+        animator.setTarget(this);
+        animator.start();
+    }
+
+    private Animator getRtAnimatorToRadius(float circleRadius) {
+        RenderNodeAnimator animator = new RenderNodeAnimator(mHwCircleRadius, circleRadius);
+        animator.setTarget(this);
+        return animator;
+    }
+
+    private void initHwProperties() {
+        mHwCenterX = CanvasProperty.createFloat(mCenterX);
+        mHwCenterY = CanvasProperty.createFloat(mCenterY);
+        mHwCirclePaint = CanvasProperty.createPaint(mCirclePaint);
+        mHwCircleRadius = CanvasProperty.createFloat(mCircleRadius);
+    }
+
     private float getMaxCircleSize() {
         getLocationInWindow(mTempPoint);
         float rootWidth = getRootView().getWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2fe98bb..4bc317a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -196,6 +196,7 @@
     private boolean mCollapsedOnDown;
     private int mPositionMinSideMargin;
     private int mLastOrientation = -1;
+    private boolean mClosingWithAlphaFadeOut;
 
     private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
         @Override
@@ -527,6 +528,7 @@
     protected void flingToHeight(float vel, boolean expand, float target,
             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
         mHeadsUpTouchHelper.notifyFling(!expand);
+        setClosingWithAlphaFadeout(!expand && getFadeoutAlpha() == 1.0f);
         super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
     }
 
@@ -638,10 +640,9 @@
 
     @Override
     protected boolean isInContentBounds(float x, float y) {
-        float yTransformed = y - mNotificationStackScroller.getY();
         float stackScrollerX = mNotificationStackScroller.getX();
-        return mNotificationStackScroller.isInContentBounds(yTransformed) && stackScrollerX < x
-                && x < stackScrollerX + mNotificationStackScroller.getWidth();
+        return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
+                && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
     }
 
     private void initDownStates(MotionEvent event) {
@@ -1074,8 +1075,12 @@
     };
 
     private void animateHeaderSlidingIn() {
-        mHeaderAnimating = true;
-        getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+        // If the QS is already expanded we don't need to slide in the header as it's already
+        // visible.
+        if (!mQsExpanded) {
+            mHeaderAnimating = true;
+            getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+        }
     }
 
     private void animateHeaderSlidingOut() {
@@ -1585,26 +1590,22 @@
         }
     }
     private void updateNotificationTranslucency() {
-        float alpha;
-        if (mExpandingFromHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
-            alpha = 1f;
-        } else {
-            alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
-                    / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
-                    - mNotificationStackScroller.getCollapseSecondCardPadding());
-            alpha = Math.max(0, Math.min(alpha, 1));
-            alpha = (float) Math.pow(alpha, 0.75);
-        }
-
-        if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
-            mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
-        } else if (alpha == 1f
-                && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
-            mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+        float alpha = 1f;
+        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+            alpha = getFadeoutAlpha();
         }
         mNotificationStackScroller.setAlpha(alpha);
     }
 
+    private float getFadeoutAlpha() {
+        float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
+                / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
+                - mNotificationStackScroller.getCollapseSecondCardPadding());
+        alpha = Math.max(0, Math.min(alpha, 1));
+        alpha = (float) Math.pow(alpha, 0.75);
+        return alpha;
+    }
+
     @Override
     protected float getOverExpansionAmount() {
         return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
@@ -2261,6 +2262,12 @@
     protected void onClosingFinished() {
         super.onClosingFinished();
         resetVerticalPanelPosition();
+        setClosingWithAlphaFadeout(false);
+    }
+
+    private void setClosingWithAlphaFadeout(boolean closing) {
+        mClosingWithAlphaFadeOut = closing;
+        mNotificationStackScroller.forceNoOverlappingRendering(closing);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6d35ff0..9d4997c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -777,6 +777,15 @@
 
     public void setExpandedFraction(float frac) {
         setExpandedHeight(getMaxPanelHeight() * frac);
+        if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+                && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+            if (frac == 0.0f) {
+                Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+                        + Log.getStackTraceString(new Throwable()));
+            } else if (frac == 1.0f) {
+                mStatusBar.endWindowManagerLogging();
+            }
+        }
     }
 
     public float getExpandedHeight() {
@@ -808,6 +817,11 @@
     }
 
     public void collapse(boolean delayed, float speedUpFactor) {
+        if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+                && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+            Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+                    + Log.getStackTraceString(new Throwable()));
+        }
         if (DEBUG) logf("collapse: " + this);
         if (mPeekPending || mPeekAnimator != null) {
             mCollapseAfterPeek = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index def95aa..7c7bec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -185,6 +185,7 @@
         HeadsUpManager.OnHeadsUpChangedListener {
     static final String TAG = "PhoneStatusBar";
     public static final boolean DEBUG = BaseStatusBar.DEBUG;
+    public static final boolean DEBUG_EMPTY_KEYGUARD = true;
     public static final boolean SPEW = false;
     public static final boolean DUMPTRUCK = true; // extra dumpsys info
     public static final boolean DEBUG_GESTURES = false;
@@ -2003,6 +2004,10 @@
         mStatusBarWindowManager.setPanelExpanded(isExpanded);
     }
 
+    public void endWindowManagerLogging() {
+        mStatusBarWindowManager.setLogState(false);
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -3547,6 +3552,9 @@
         // Make our window larger and the panel expanded.
         makeExpandedVisible(true);
         mNotificationPanel.instantExpand();
+        if (DEBUG_EMPTY_KEYGUARD) {
+            mStatusBarWindowManager.setLogState(true);
+        }
     }
 
     private void instantCollapseNotificationPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index aa499ad..b7e675d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -20,12 +20,14 @@
 import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.util.EventLog;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarState;
 
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
@@ -108,7 +110,11 @@
     @Override
     public void onAllPanelsCollapsed() {
         super.onAllPanelsCollapsed();
-
+        if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+                && mBar.getBarState() == StatusBarState.KEYGUARD) {
+            Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+                    + Log.getStackTraceString(new Throwable()));
+        }
         // Close the status bar in the next frame so we can show the end of the animation.
         postOnAnimation(new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index de426430..58017d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.os.SystemProperties;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -31,6 +32,8 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.StatusBarState;
 
+import java.lang.reflect.Field;
+
 /**
  * Encapsulates all logic for the status bar window state management.
  */
@@ -45,6 +48,7 @@
     private final boolean mKeyguardScreenRotation;
 
     private final State mCurrentState = new State();
+    private boolean mLogState;
 
     public StatusBarWindowManager(Context context) {
         mContext = context;
@@ -129,9 +133,7 @@
     }
 
     private void applyHeight(State state) {
-        boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
-                || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
-                || state.headsUpShowing);
+        boolean expanded = isExpanded(state);
         if (expanded) {
             mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
         } else {
@@ -139,6 +141,12 @@
         }
     }
 
+    private boolean isExpanded(State state) {
+        return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
+                || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
+                || state.headsUpShowing);
+    }
+
     private void applyFitsSystemWindows(State state) {
         mStatusBarView.setFitsSystemWindows(!state.isKeyguardShowingAndNotOccluded());
     }
@@ -176,6 +184,9 @@
         applyFitsSystemWindows(state);
         applyModalFlag(state);
         if (mLp.copyFrom(mLpChanged) != 0) {
+            if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD && mLogState) {
+                logCurrentState();
+            }
             mWindowManager.updateViewLayout(mStatusBarView, mLp);
         }
     }
@@ -272,6 +283,21 @@
         apply(mCurrentState);
     }
 
+    public void setLogState(boolean logState) {
+        mLogState = logState;
+        if (logState) {
+            Log.w(PhoneStatusBar.TAG, "===== Started logging WM state changes =====");
+            logCurrentState();
+        } else {
+            Log.w(PhoneStatusBar.TAG, "===== Finished logging WM state changes =====");
+        }
+    }
+
+    private void logCurrentState() {
+        Log.i(PhoneStatusBar.TAG, mCurrentState.toString()
+                + "\n  Expanded: " + isExpanded(mCurrentState));
+    }
+
     private static class State {
         boolean keyguardShowing;
         boolean keyguardOccluded;
@@ -294,5 +320,31 @@
         private boolean isKeyguardShowingAndNotOccluded() {
             return keyguardShowing && !keyguardOccluded;
         }
+
+        @Override
+        public String toString() {
+            StringBuilder result = new StringBuilder();
+            String newLine = "\n";
+            result.append("Window State {");
+            result.append(newLine);
+
+            Field[] fields = this.getClass().getDeclaredFields();
+
+            // Print field names paired with their values
+            for (Field field : fields) {
+                result.append("  ");
+                try {
+                    result.append(field.getName());
+                    result.append(": ");
+                    //requires access to private field:
+                    result.append(field.get(this));
+                } catch (IllegalAccessException ex) {
+                }
+                result.append(newLine);
+            }
+            result.append("}");
+
+            return result.toString();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 3b91751..6a8f8ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -171,7 +171,6 @@
         if (mNotificationPanel.isFullyExpanded()
                 && mStackScrollLayout.getVisibility() == View.VISIBLE
                 && mService.getBarState() == StatusBarState.KEYGUARD
-                && !mService.isQsExpanded()
                 && !mService.isBouncerShowing()) {
             intercept = mDragDownHelper.onInterceptTouchEvent(ev);
             // wake up on a touch down event, if dozing
@@ -195,7 +194,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean handled = false;
-        if (mService.getBarState() == StatusBarState.KEYGUARD && !mService.isQsExpanded()) {
+        if (mService.getBarState() == StatusBarState.KEYGUARD) {
             handled = mDragDownHelper.onTouchEvent(ev);
         }
         if (!handled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 4ae800f..d8f6bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -225,6 +225,7 @@
     private HeadsUpManager mHeadsUpManager;
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
+    private boolean mForceNoOverlappingRendering;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -811,8 +812,7 @@
         }
         handleEmptySpaceClick(ev);
         boolean expandWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion
-                && isScrollingEnabled()) {
+        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
             if (isCancelOrUp) {
                 mExpandHelper.onlyObserveMovements(false);
             }
@@ -1566,7 +1566,7 @@
         initDownStates(ev);
         handleEmptySpaceClick(ev);
         boolean expandWantsIt = false;
-        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) {
+        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
             expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
         }
         boolean scrollWantsIt = false;
@@ -2267,11 +2267,11 @@
     private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
         if (view instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            if (row.isUserLocked()) {
+            if (row.isUserLocked() && row != getFirstChildNotGone()) {
                 // We are actually expanding this view
                 float endPosition = row.getTranslationY() + row.getActualHeight();
                 int stackEnd = mMaxLayoutHeight - mBottomStackPeekSize -
-                        mBottomStackSlowDownHeight;
+                        mBottomStackSlowDownHeight + (int) mStackTranslation;
                 if (endPosition > stackEnd) {
                     mOwnScrollY += endPosition - stackEnd;
                     mDisallowScrollingInThisMotion = true;
@@ -2614,7 +2614,7 @@
         }
     }
 
-    private boolean isBelowLastNotification(float touchX, float touchY) {
+    public boolean isBelowLastNotification(float touchX, float touchY) {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableView child = (ExpandableView) getChildAt(i);
@@ -2640,7 +2640,7 @@
                 }
             }
         }
-        return touchY > mIntrinsicPadding;
+        return touchY > mTopPadding + mStackTranslation;
     }
 
     private void updateExpandButtons() {
@@ -2732,6 +2732,15 @@
         mScrimController = scrimController;
     }
 
+    public void forceNoOverlappingRendering(boolean force) {
+        mForceNoOverlappingRendering = force;
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index bea138e..0a50593 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -51,6 +51,7 @@
  * <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
  * </div>
  **/
+
 public class Allocation extends BaseObj {
     Type mType;
     Bitmap mBitmap;
@@ -455,28 +456,31 @@
      *
      */
     public void syncAll(int srcLocation) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "syncAll");
-        switch (srcLocation) {
-        case USAGE_GRAPHICS_TEXTURE:
-        case USAGE_SCRIPT:
-            if ((mUsage & USAGE_SHARED) != 0) {
-                copyFrom(mBitmap);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "syncAll");
+            switch (srcLocation) {
+                case USAGE_GRAPHICS_TEXTURE:
+                case USAGE_SCRIPT:
+                    if ((mUsage & USAGE_SHARED) != 0) {
+                        copyFrom(mBitmap);
+                    }
+                    break;
+                case USAGE_GRAPHICS_CONSTANTS:
+                case USAGE_GRAPHICS_VERTEX:
+                    break;
+                case USAGE_SHARED:
+                    if ((mUsage & USAGE_SHARED) != 0) {
+                        copyTo(mBitmap);
+                    }
+                    break;
+                default:
+                    throw new RSIllegalArgumentException("Source must be exactly one usage type.");
             }
-            break;
-        case USAGE_GRAPHICS_CONSTANTS:
-        case USAGE_GRAPHICS_VERTEX:
-            break;
-        case USAGE_SHARED:
-            if ((mUsage & USAGE_SHARED) != 0) {
-                copyTo(mBitmap);
-            }
-            break;
-        default:
-            throw new RSIllegalArgumentException("Source must be exactly one usage type.");
+            mRS.validate();
+            mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.validate();
-        mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -487,14 +491,17 @@
      *
      */
     public void ioSend() {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "ioSend");
-        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
-            throw new RSIllegalArgumentException(
-                "Can only send buffer if IO_OUTPUT usage specified.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "ioSend");
+            if ((mUsage & USAGE_IO_OUTPUT) == 0) {
+                throw new RSIllegalArgumentException(
+                    "Can only send buffer if IO_OUTPUT usage specified.");
+            }
+            mRS.validate();
+            mRS.nAllocationIoSend(getID(mRS));
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.validate();
-        mRS.nAllocationIoSend(getID(mRS));
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -503,14 +510,17 @@
      *
      */
     public void ioReceive() {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "ioReceive");
-        if ((mUsage & USAGE_IO_INPUT) == 0) {
-            throw new RSIllegalArgumentException(
-                "Can only receive if IO_INPUT usage specified.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "ioReceive");
+            if ((mUsage & USAGE_IO_INPUT) == 0) {
+                throw new RSIllegalArgumentException(
+                    "Can only receive if IO_INPUT usage specified.");
+            }
+            mRS.validate();
+            mRS.nAllocationIoReceive(getID(mRS));
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.validate();
-        mRS.nAllocationIoReceive(getID(mRS));
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -519,28 +529,31 @@
      * @param d Source array.
      */
     public void copyFrom(BaseObj[] d) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
-        mRS.validate();
-        validateIsObject();
-        if (d.length != mCurrentCount) {
-            throw new RSIllegalArgumentException("Array size mismatch, allocation sizeX = " +
-                                                 mCurrentCount + ", array length = " + d.length);
-        }
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+            mRS.validate();
+            validateIsObject();
+            if (d.length != mCurrentCount) {
+                throw new RSIllegalArgumentException("Array size mismatch, allocation sizeX = " +
+                                                      mCurrentCount + ", array length = " + d.length);
+            }
 
-        if (RenderScript.sPointerSize == 8) {
-            long i[] = new long[d.length * 4];
-            for (int ct=0; ct < d.length; ct++) {
-                i[ct * 4] = d[ct].getID(mRS);
+            if (RenderScript.sPointerSize == 8) {
+                long i[] = new long[d.length * 4];
+                for (int ct=0; ct < d.length; ct++) {
+                    i[ct * 4] = d[ct].getID(mRS);
+                }
+                copy1DRangeFromUnchecked(0, mCurrentCount, i);
+            } else {
+                int i[] = new int[d.length];
+                for (int ct=0; ct < d.length; ct++) {
+                    i[ct] = (int) d[ct].getID(mRS);
+                }
+                copy1DRangeFromUnchecked(0, mCurrentCount, i);
             }
-            copy1DRangeFromUnchecked(0, mCurrentCount, i);
-        } else {
-            int i[] = new int[d.length];
-            for (int ct=0; ct < d.length; ct++) {
-                i[ct] = (int)d[ct].getID(mRS);
-            }
-            copy1DRangeFromUnchecked(0, mCurrentCount, i);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     private void validateBitmapFormat(Bitmap b) {
@@ -599,16 +612,19 @@
     }
 
     private void copyFromUnchecked(Object array, Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
-        } else {
-            copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
+            mRS.validate();
+            if (mCurrentDimZ > 0) {
+                copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
+            } else if (mCurrentDimY > 0) {
+                copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
+            } else {
+                copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
+            }
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -619,10 +635,13 @@
      * @param array The source data array
      */
     public void copyFromUnchecked(Object array) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
-        copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
-                          java.lang.reflect.Array.getLength(array));
-        Trace.traceEnd(RenderScript.TRACE_TAG);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
+            copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
+                              java.lang.reflect.Array.getLength(array));
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
+        }
     }
 
     /**
@@ -679,10 +698,13 @@
      * @param array The source data array
      */
     public void copyFrom(Object array) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
-        copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
-                          java.lang.reflect.Array.getLength(array));
-        Trace.traceEnd(RenderScript.TRACE_TAG);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+            copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
+                              java.lang.reflect.Array.getLength(array));
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
+        }
     }
 
     /**
@@ -747,19 +769,22 @@
      * @param b the source bitmap
      */
     public void copyFrom(Bitmap b) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
-        mRS.validate();
-        if (b.getConfig() == null) {
-            Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(newBitmap);
-            c.drawBitmap(b, 0, 0, null);
-            copyFrom(newBitmap);
-            return;
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+            mRS.validate();
+            if (b.getConfig() == null) {
+                Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(newBitmap);
+                c.drawBitmap(b, 0, 0, null);
+                copyFrom(newBitmap);
+                return;
+            }
+            validateBitmapSize(b);
+            validateBitmapFormat(b);
+            mRS.nAllocationCopyFromBitmap(getID(mRS), b);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        validateBitmapSize(b);
-        validateBitmapFormat(b);
-        mRS.nAllocationCopyFromBitmap(getID(mRS), b);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -769,13 +794,16 @@
      * @param a the source allocation
      */
     public void copyFrom(Allocation a) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
-        mRS.validate();
-        if (!mType.equals(a.getType())) {
-            throw new RSIllegalArgumentException("Types of allocations must match.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+            mRS.validate();
+            if (!mType.equals(a.getType())) {
+                throw new RSIllegalArgumentException("Types of allocations must match.");
+            }
+            copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -890,17 +918,20 @@
 
     private void copy1DRangeFromUnchecked(int off, int count, Object array,
                                           Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
-        final int dataSize = mType.mElement.getBytesSize() * count;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            usePadding = true;
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
+            final int dataSize = mType.mElement.getBytesSize() * count;
+            // AutoPadding for Vec3 Element
+            boolean usePadding = false;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                usePadding = true;
+            }
+            data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
+            mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
+                                  mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
-        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
-                              mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -1074,28 +1105,31 @@
 
     void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, Object array,
                                   Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        final int dataSize = mType.mElement.getBytesSize() * w * h;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
+            mRS.validate();
+            validate2DRange(xoff, yoff, w, h);
+            final int dataSize = mType.mElement.getBytesSize() * w * h;
+            // AutoPadding for Vec3 Element
+            boolean usePadding = false;
+            int sizeBytes = arrayLen * dt.mSize;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                if (dataSize / 4 * 3 > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
+                usePadding = true;
+                sizeBytes = dataSize;
+            } else {
+                if (dataSize > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
             }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
+            mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+                                  array, sizeBytes, dt,
+                                  mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
-                              array, sizeBytes, dt,
-                              mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -1109,11 +1143,14 @@
      * @param array Data to be placed into the Allocation
      */
     public void copy2DRangeFrom(int xoff, int yoff, int w, int h, Object array) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
-                                 validateObjectIsPrimitiveArray(array, true),
-                                 java.lang.reflect.Array.getLength(array));
-        Trace.traceEnd(RenderScript.TRACE_TAG);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
+            copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
+                                     validateObjectIsPrimitiveArray(array, true),
+                                     java.lang.reflect.Array.getLength(array));
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
+        }
     }
 
     /**
@@ -1194,14 +1231,17 @@
      */
     public void copy2DRangeFrom(int xoff, int yoff, int w, int h,
                                 Allocation data, int dataXoff, int dataYoff) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
-                              mSelectedLOD, mSelectedFace.mID,
-                              w, h, data.getID(mRS), dataXoff, dataYoff,
-                              data.mSelectedLOD, data.mSelectedFace.mID);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
+            mRS.validate();
+            validate2DRange(xoff, yoff, w, h);
+            mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
+                                  mSelectedLOD, mSelectedFace.mID,
+                                  w, h, data.getID(mRS), dataXoff, dataYoff,
+                                  data.mSelectedLOD, data.mSelectedFace.mID);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
+        }
     }
 
     /**
@@ -1258,28 +1298,31 @@
      */
     private void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
                                           Object array, Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFromUnchecked");
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        final int dataSize = mType.mElement.getBytesSize() * w * h * d;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFromUnchecked");
+            mRS.validate();
+            validate3DRange(xoff, yoff, zoff, w, h, d);
+            final int dataSize = mType.mElement.getBytesSize() * w * h * d;
+            // AutoPadding for Vec3 Element
+            boolean usePadding = false;
+            int sizeBytes = arrayLen * dt.mSize;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                if (dataSize / 4 * 3 > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
+                usePadding = true;
+                sizeBytes = dataSize;
+            } else {
+                if (dataSize > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
             }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
+            mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+                                  array, sizeBytes, dt,
+                                  mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
-                              array, sizeBytes, dt,
-                              mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -1295,11 +1338,14 @@
      * @param array to be placed into the allocation
      */
     public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFrom");
-        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
-                                 validateObjectIsPrimitiveArray(array, true),
-                                 java.lang.reflect.Array.getLength(array));
-        Trace.traceEnd(RenderScript.TRACE_TAG);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFrom");
+            copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
+                                     validateObjectIsPrimitiveArray(array, true),
+                                     java.lang.reflect.Array.getLength(array));
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
+        }
     }
 
     /**
@@ -1334,34 +1380,40 @@
      * @param b The bitmap to be set from the Allocation.
      */
     public void copyTo(Bitmap b) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
-        mRS.validate();
-        validateBitmapFormat(b);
-        validateBitmapSize(b);
-        mRS.nAllocationCopyToBitmap(getID(mRS), b);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
+            mRS.validate();
+            validateBitmapFormat(b);
+            validateBitmapSize(b);
+            mRS.nAllocationCopyToBitmap(getID(mRS), b);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
+        }
     }
 
     private void copyTo(Object array, Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
-        mRS.validate();
-        boolean usePadding = false;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            usePadding = true;
-        }
-        if (usePadding) {
-            if (dt.mSize * arrayLen < mSize / 4 * 3) {
-                throw new RSIllegalArgumentException(
-                    "Size of output array cannot be smaller than size of allocation.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
+            mRS.validate();
+            boolean usePadding = false;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                usePadding = true;
             }
-        } else {
-            if (dt.mSize * arrayLen < mSize) {
-                throw new RSIllegalArgumentException(
-                    "Size of output array cannot be smaller than size of allocation.");
+            if (usePadding) {
+                if (dt.mSize * arrayLen < mSize / 4 * 3) {
+                    throw new RSIllegalArgumentException(
+                        "Size of output array cannot be smaller than size of allocation.");
+                }
+            } else {
+                if (dt.mSize * arrayLen < mSize) {
+                    throw new RSIllegalArgumentException(
+                        "Size of output array cannot be smaller than size of allocation.");
+                }
             }
+            mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -1499,17 +1551,20 @@
 
     private void copy1DRangeToUnchecked(int off, int count, Object array,
                                         Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeToUnchecked");
-        final int dataSize = mType.mElement.getBytesSize() * count;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            usePadding = true;
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeToUnchecked");
+            final int dataSize = mType.mElement.getBytesSize() * count;
+            // AutoPadding for Vec3 Element
+            boolean usePadding = false;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                usePadding = true;
+            }
+            data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
+            mRS.nAllocationRead1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
+                                  mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
-        mRS.nAllocationRead1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
-                              mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -1649,27 +1704,30 @@
 
     void copy2DRangeToUnchecked(int xoff, int yoff, int w, int h, Object array,
                                 Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeToUnchecked");
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        final int dataSize = mType.mElement.getBytesSize() * w * h;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeToUnchecked");
+            mRS.validate();
+            validate2DRange(xoff, yoff, w, h);
+            final int dataSize = mType.mElement.getBytesSize() * w * h;
+            // AutoPadding for Vec3 Element
+            boolean usePadding = false;
+            int sizeBytes = arrayLen * dt.mSize;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                if (dataSize / 4 * 3 > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
+                usePadding = true;
+                sizeBytes = dataSize;
+            } else {
+                if (dataSize > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
             }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
+            mRS.nAllocationRead2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+                                  array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.nAllocationRead2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
-                              array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /**
@@ -1757,27 +1815,30 @@
      */
     private void copy3DRangeToUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
                                         Object array, Element.DataType dt, int arrayLen) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeToUnchecked");
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        final int dataSize = mType.mElement.getBytesSize() * w * h * d;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeToUnchecked");
+            mRS.validate();
+            validate3DRange(xoff, yoff, zoff, w, h, d);
+            final int dataSize = mType.mElement.getBytesSize() * w * h * d;
+            // AutoPadding for Vec3 Element
+            boolean usePadding = false;
+            int sizeBytes = arrayLen * dt.mSize;
+            if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
+                if (dataSize / 4 * 3 > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
+                usePadding = true;
+                sizeBytes = dataSize;
+            } else {
+                if (dataSize > sizeBytes) {
+                    throw new RSIllegalArgumentException("Array too small for allocation type.");
+                }
             }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
+            mRS.nAllocationRead3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+                                  array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        mRS.nAllocationRead3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
-                              array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
-        Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
     /*
@@ -1815,17 +1876,20 @@
      *              utilized
      */
     static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "createTyped");
-        rs.validate();
-        if (type.getID(rs) == 0) {
-            throw new RSInvalidStateException("Bad Type");
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "createTyped");
+            rs.validate();
+            if (type.getID(rs) == 0) {
+                throw new RSInvalidStateException("Bad Type");
+            }
+            long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
+            if (id == 0) {
+                throw new RSRuntimeException("Allocation creation failed.");
+            }
+            return new Allocation(id, rs, type, usage);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
-        if (id == 0) {
-            throw new RSRuntimeException("Allocation creation failed.");
-        }
-        Trace.traceEnd(RenderScript.TRACE_TAG);
-        return new Allocation(id, rs, type, usage);
     }
 
     /**
@@ -1869,18 +1933,21 @@
      */
     static public Allocation createSized(RenderScript rs, Element e,
                                          int count, int usage) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "createSized");
-        rs.validate();
-        Type.Builder b = new Type.Builder(rs, e);
-        b.setX(count);
-        Type t = b.create();
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "createSized");
+            rs.validate();
+            Type.Builder b = new Type.Builder(rs, e);
+            b.setX(count);
+            Type t = b.create();
 
-        long id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
-        if (id == 0) {
-            throw new RSRuntimeException("Allocation creation failed.");
+            long id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
+            if (id == 0) {
+                throw new RSRuntimeException("Allocation creation failed.");
+            }
+            return new Allocation(id, rs, t, usage);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-        Trace.traceEnd(RenderScript.TRACE_TAG);
-        return new Allocation(id, rs, t, usage);
     }
 
     /**
@@ -1939,44 +2006,47 @@
     static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
                                               MipmapControl mips,
                                               int usage) {
-        Trace.traceBegin(RenderScript.TRACE_TAG, "createFromBitmap");
-        rs.validate();
+        try {
+            Trace.traceBegin(RenderScript.TRACE_TAG, "createFromBitmap");
+            rs.validate();
 
-        // WAR undocumented color formats
-        if (b.getConfig() == null) {
-            if ((usage & USAGE_SHARED) != 0) {
-                throw new RSIllegalArgumentException("USAGE_SHARED cannot be used with a Bitmap that has a null config.");
+            // WAR undocumented color formats
+            if (b.getConfig() == null) {
+                if ((usage & USAGE_SHARED) != 0) {
+                    throw new RSIllegalArgumentException("USAGE_SHARED cannot be used with a Bitmap that has a null config.");
+                }
+                Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(newBitmap);
+                c.drawBitmap(b, 0, 0, null);
+                return createFromBitmap(rs, newBitmap, mips, usage);
             }
-            Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(newBitmap);
-            c.drawBitmap(b, 0, 0, null);
-            return createFromBitmap(rs, newBitmap, mips, usage);
-        }
 
-        Type t = typeFromBitmap(rs, b, mips);
+            Type t = typeFromBitmap(rs, b, mips);
 
-        // enable optimized bitmap path only with no mipmap and script-only usage
-        if (mips == MipmapControl.MIPMAP_NONE &&
-            t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
-            usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
-            long id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
+            // enable optimized bitmap path only with no mipmap and script-only usage
+            if (mips == MipmapControl.MIPMAP_NONE &&
+                 t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
+                 usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
+                long id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
+                if (id == 0) {
+                    throw new RSRuntimeException("Load failed.");
+                }
+
+                // keep a reference to the Bitmap around to prevent GC
+                Allocation alloc = new Allocation(id, rs, t, usage);
+                alloc.setBitmap(b);
+                return alloc;
+            }
+
+
+            long id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
             if (id == 0) {
                 throw new RSRuntimeException("Load failed.");
             }
-
-            // keep a reference to the Bitmap around to prevent GC
-            Allocation alloc = new Allocation(id, rs, t, usage);
-            alloc.setBitmap(b);
-            return alloc;
+            return new Allocation(id, rs, t, usage);
+        } finally {
+            Trace.traceEnd(RenderScript.TRACE_TAG);
         }
-
-
-        long id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
-        if (id == 0) {
-            throw new RSRuntimeException("Load failed.");
-        }
-        Trace.traceEnd(RenderScript.TRACE_TAG);
-        return new Allocation(id, rs, t, usage);
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d4c3194..7bcbcfb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -166,8 +166,6 @@
     private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
             new ArrayList<>();
 
-    private final Region mTempRegion = new Region();
-
     private final Rect mTempRect = new Rect();
 
     private final Rect mTempRect1 = new Rect();
@@ -2241,7 +2239,7 @@
                 throws RemoteException {
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
-            Region partialInteractiveRegion = mTempRegion;
+            Region partialInteractiveRegion = Region.obtain();
             synchronized (mLock) {
                 // We treat calls from a profile as if made by its parent as profiles
                 // share the accessibility state of the parent. The call below
@@ -2265,6 +2263,7 @@
                 }
                 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
                         resolvedWindowId, partialInteractiveRegion)) {
+                    partialInteractiveRegion.recycle();
                     partialInteractiveRegion = null;
                 }
             }
@@ -2282,6 +2281,10 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
+                // Recycle if passed to another process.
+                if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+                    partialInteractiveRegion.recycle();
+                }
             }
             return false;
         }
@@ -2293,7 +2296,7 @@
                 throws RemoteException {
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
-            Region partialInteractiveRegion = mTempRegion;
+            Region partialInteractiveRegion = Region.obtain();
             synchronized (mLock) {
                 // We treat calls from a profile as if made by its parent as profiles
                 // share the accessibility state of the parent. The call below
@@ -2317,6 +2320,7 @@
                 }
                 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
                         resolvedWindowId, partialInteractiveRegion)) {
+                    partialInteractiveRegion.recycle();
                     partialInteractiveRegion = null;
                 }
             }
@@ -2334,6 +2338,10 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
+                // Recycle if passed to another process.
+                if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+                    partialInteractiveRegion.recycle();
+                }
             }
             return false;
         }
@@ -2345,7 +2353,7 @@
                 long interrogatingTid) throws RemoteException {
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
-            Region partialInteractiveRegion = mTempRegion;
+            Region partialInteractiveRegion = Region.obtain();
             synchronized (mLock) {
                 // We treat calls from a profile as if made by its parent as profiles
                 // share the accessibility state of the parent. The call below
@@ -2369,6 +2377,7 @@
                 }
                 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
                         resolvedWindowId, partialInteractiveRegion)) {
+                    partialInteractiveRegion.recycle();
                     partialInteractiveRegion = null;
                 }
             }
@@ -2386,6 +2395,10 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
+                // Recycle if passed to another process.
+                if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+                    partialInteractiveRegion.recycle();
+                }
             }
             return false;
         }
@@ -2397,7 +2410,7 @@
                 throws RemoteException {
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
-            Region partialInteractiveRegion = mTempRegion;
+            Region partialInteractiveRegion = Region.obtain();
             synchronized (mLock) {
                 // We treat calls from a profile as if made by its parent as profiles
                 // share the accessibility state of the parent. The call below
@@ -2422,6 +2435,7 @@
                 }
                 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
                         resolvedWindowId, partialInteractiveRegion)) {
+                    partialInteractiveRegion.recycle();
                     partialInteractiveRegion = null;
                 }
             }
@@ -2439,6 +2453,10 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
+                // Recycle if passed to another process.
+                if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+                    partialInteractiveRegion.recycle();
+                }
             }
             return false;
         }
@@ -2450,7 +2468,7 @@
                 throws RemoteException {
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
-            Region partialInteractiveRegion = mTempRegion;
+            Region partialInteractiveRegion = Region.obtain();
             synchronized (mLock) {
                 // We treat calls from a profile as if made by its parent as profiles
                 // share the accessibility state of the parent. The call below
@@ -2474,6 +2492,7 @@
                 }
                 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
                         resolvedWindowId, partialInteractiveRegion)) {
+                    partialInteractiveRegion.recycle();
                     partialInteractiveRegion = null;
                 }
             }
@@ -2491,6 +2510,10 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
+                // Recycle if passed to another process.
+                if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+                    partialInteractiveRegion.recycle();
+                }
             }
             return false;
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e07e4e2..44d00d7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1661,6 +1661,12 @@
     }
 
     private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
+    private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
+
+    // Overridden for testing purposes to avoid writing to SystemProperties.
+    protected int getDefaultTcpRwnd() {
+        return SystemProperties.getInt(DEFAULT_TCP_RWND_KEY, 0);
+    }
 
     private void updateTcpBufferSizes(NetworkAgentInfo nai) {
         if (isDefaultNetwork(nai) == false) {
@@ -1696,10 +1702,8 @@
             loge("Can't set TCP buffer sizes:" + e);
         }
 
-        final String defaultRwndKey = "net.tcp.default_init_rwnd";
-        int defaultRwndValue = SystemProperties.getInt(defaultRwndKey, 0);
         Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
-            Settings.Global.TCP_DEFAULT_INIT_RWND, defaultRwndValue);
+            Settings.Global.TCP_DEFAULT_INIT_RWND, getDefaultTcpRwnd());
         final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
         if (rwndValue != 0) {
             SystemProperties.set(sysctlKey, rwndValue.toString());
@@ -3819,10 +3823,12 @@
 //        }
         updateTcpBufferSizes(networkAgent);
 
-        // TODO: deprecate and remove mDefaultDns when we can do so safely.
-        // For now, use it only when the network has Internet access. http://b/18327075
-        final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
-                NET_CAPABILITY_INTERNET);
+        // TODO: deprecate and remove mDefaultDns when we can do so safely. See http://b/18327075
+        // In L, we used it only when the network had Internet access but provided no DNS servers.
+        // For now, just disable it, and if disabling it doesn't break things, remove it.
+        // final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
+        //        NET_CAPABILITY_INTERNET);
+        final boolean useDefaultDns = false;
         final boolean flushDns = updateRoutes(newLp, oldLp, netId);
         updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
 
@@ -4503,7 +4509,7 @@
             info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo());
             sendConnectedBroadcast(info);
         } else {
-            info.setDetailedState(DetailedState.DISCONNECTED, null, info.getExtraInfo());
+            info.setDetailedState(DetailedState.DISCONNECTED, info.getReason(), info.getExtraInfo());
             Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
             intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
             intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d48953d..8c8be4e 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -180,6 +180,7 @@
     private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark";
 
     private static final String VOLD_TAG = "VoldConnector";
+    private static final String CRYPTD_TAG = "CryptdConnector";
 
     /** Maximum number of ASEC containers allowed to be mounted. */
     private static final int MAX_CONTAINERS = 250;
@@ -367,6 +368,7 @@
 
     private final Context mContext;
     private final NativeDaemonConnector mConnector;
+    private final NativeDaemonConnector mCryptConnector;
 
     private volatile boolean mSystemReady = false;
     private volatile boolean mDaemonConnected = false;
@@ -375,7 +377,8 @@
 
     private final Callbacks mCallbacks;
 
-    private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
+    // Two connectors - mConnector & mCryptConnector
+    private final CountDownLatch mConnectedSignal = new CountDownLatch(2);
     private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
 
     private final Object mUnmountLock = new Object();
@@ -754,6 +757,10 @@
          * the hounds!
          */
         mConnectedSignal.countDown();
+        if (mConnectedSignal.getCount() != 0) {
+            // More daemons need to connect
+            return;
+        }
 
         // On an encrypted device we can't see system properties yet, so pull
         // the system locale out of the mount service.
@@ -1186,6 +1193,7 @@
          * amount of containers we'd ever expect to have. This keeps an
          * "asec list" from blocking a thread repeatedly.
          */
+
         mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
                 null);
         mConnector.setDebug(true);
@@ -1193,6 +1201,14 @@
         Thread thread = new Thread(mConnector, VOLD_TAG);
         thread.start();
 
+        // Reuse parameters from first connector since they are tested and safe
+        mCryptConnector = new NativeDaemonConnector(this, "cryptd",
+                MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
+        mCryptConnector.setDebug(true);
+
+        Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);
+        crypt_thread.start();
+
         // Add ourself to the Watchdog monitors if enabled.
         if (WATCHDOG_ENABLE) {
             Watchdog.getInstance().addMonitor(this);
@@ -2049,7 +2065,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "cryptocomplete");
+            event = mCryptConnector.execute("cryptfs", "cryptocomplete");
             return Integer.parseInt(event.getMessage());
         } catch (NumberFormatException e) {
             // Bad result - unexpected.
@@ -2096,7 +2112,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
+            event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
 
             final int code = Integer.parseInt(event.getMessage());
             if (code == 0) {
@@ -2105,7 +2121,7 @@
                 mHandler.postDelayed(new Runnable() {
                     public void run() {
                         try {
-                            mConnector.execute("cryptfs", "restart");
+                            mCryptConnector.execute("cryptfs", "restart");
                         } catch (NativeDaemonConnectorException e) {
                             Slog.e(TAG, "problem executing in background", e);
                         }
@@ -2135,7 +2151,7 @@
         }
 
         try {
-            mConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
+            mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
                                new SensitiveArg(toHex(password)));
         } catch (NativeDaemonConnectorException e) {
             // Encryption failed
@@ -2160,7 +2176,7 @@
         }
 
         try {
-            NativeDaemonEvent event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
+            NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
                         new SensitiveArg(toHex(password)));
             return Integer.parseInt(event.getMessage());
         } catch (NativeDaemonConnectorException e) {
@@ -2194,7 +2210,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
+            event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
             Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
             return Integer.parseInt(event.getMessage());
         } catch (NativeDaemonConnectorException e) {
@@ -2214,7 +2230,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "getpwtype");
+            event = mCryptConnector.execute("cryptfs", "getpwtype");
             for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
                 if (CRYPTO_TYPES[i].equals(event.getMessage()))
                     return i;
@@ -2238,7 +2254,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "setfield", field, contents);
+            event = mCryptConnector.execute("cryptfs", "setfield", field, contents);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -2257,7 +2273,7 @@
         final NativeDaemonEvent event;
         try {
             final String[] contents = NativeDaemonEvent.filterMessageList(
-                    mConnector.executeForList("cryptfs", "getfield", field),
+                    mCryptConnector.executeForList("cryptfs", "getfield", field),
                     VoldResponseCode.CryptfsGetfieldResult);
             String result = new String();
             for (String content : contents) {
@@ -2279,7 +2295,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "getpw");
+            event = mCryptConnector.execute("cryptfs", "getpw");
             if ("-1".equals(event.getMessage())) {
                 // -1 equals no password
                 return null;
@@ -2301,7 +2317,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "clearpw");
+            event = mCryptConnector.execute("cryptfs", "clearpw");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -3114,5 +3130,8 @@
         if (mConnector != null) {
             mConnector.monitor();
         }
+        if (mCryptConnector != null) {
+            mCryptConnector.monitor();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 3315c89..21f96c9 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1785,7 +1785,13 @@
 
         // Get the calling package. We will use it for the purpose of caching.
         final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
-        List<String> callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+        List<String> callerOwnedPackageNames;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
         if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
             String msg = String.format(
                     "Uid %s is attempting to illegally masquerade as package %s!",
@@ -1798,15 +1804,15 @@
         loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
         loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
 
-        // Distill the caller's package signatures into a single digest.
-        final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
-
         if (notifyOnAuthFailure) {
             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
         }
 
         long identityToken = clearCallingIdentity();
         try {
+            // Distill the caller's package signatures into a single digest.
+            final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
+
             // if the caller has permission, do the peek. otherwise go the more expensive
             // route of starting a Session
             if (!customTokens && permissionGranted) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 81b8457..64e30e5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,13 +39,13 @@
 import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.ApplicationThreadNative;
-import android.app.AssistContent;
-import android.app.AssistStructure;
 import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IAppTask;
 import android.app.ITaskStackListener;
 import android.app.ProfilerInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 745cb7e..80b8a93 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1121,25 +1121,23 @@
     }
 
     final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
-        if (r.nextReceiver > 0) {
-            Object curReceiver = r.receivers.get(r.nextReceiver-1);
+        final int logIndex = r.nextReceiver - 1;
+        if (logIndex >= 0 && logIndex < r.receivers.size()) {
+            Object curReceiver = r.receivers.get(logIndex);
             if (curReceiver instanceof BroadcastFilter) {
                 BroadcastFilter bf = (BroadcastFilter) curReceiver;
                 EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
                         bf.owningUserId, System.identityHashCode(r),
-                        r.intent.getAction(),
-                        r.nextReceiver - 1,
-                        System.identityHashCode(bf));
+                        r.intent.getAction(), logIndex, System.identityHashCode(bf));
             } else {
-                ResolveInfo ri = (ResolveInfo)curReceiver;
+                ResolveInfo ri = (ResolveInfo) curReceiver;
                 EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
                         UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
-                        System.identityHashCode(r), r.intent.getAction(),
-                        r.nextReceiver - 1, ri.toString());
+                        System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
             }
         } else {
-            Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
-                    + r);
+            if (logIndex < 0) Slog.w(TAG,
+                    "Discarding broadcast before first receiver is invoked: " + r);
             EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
                     -1, System.identityHashCode(r),
                     r.intent.getAction(),
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bf896a5..fc50e2c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -32,6 +32,7 @@
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -48,6 +49,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -177,8 +179,8 @@
     private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
 
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
-    // Default to 30s linger time-out.
-    private static final int DEFAULT_LINGER_DELAY_MS = 30000;
+    // Default to 30s linger time-out.  Modifyable only for testing.
+    private static int DEFAULT_LINGER_DELAY_MS = 30000;
     private final int mLingerDelayMs;
     private int mLingerToken = 0;
 
@@ -771,4 +773,13 @@
         mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
                 PERMISSION_ACCESS_NETWORK_CONDITIONS);
     }
+
+    // Allow tests to override linger time.
+    @VisibleForTesting
+    public static void SetDefaultLingerTime(int time_ms) {
+        if (Process.myUid() == Process.SYSTEM_UID) {
+            throw new SecurityException("SetDefaultLingerTime only for internal testing.");
+        }
+        DEFAULT_LINGER_DELAY_MS = time_ms;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index dcf668d..1cec750 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -52,6 +52,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.UserHandle;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -77,6 +78,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -163,6 +165,8 @@
     private final List<File> mResolvedStagedFiles = new ArrayList<>();
     @GuardedBy("mLock")
     private final List<File> mResolvedInheritedFiles = new ArrayList<>();
+    @GuardedBy("mLock")
+    private File mInheritedFilesBase;
 
     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
@@ -511,8 +515,13 @@
                 final List<File> fromFiles = mResolvedInheritedFiles;
                 final File toDir = resolveStageDir();
 
+                if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+                if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+                    throw new IllegalStateException("mInheritedFilesBase == null");
+                }
+
                 if (isLinkPossible(fromFiles, toDir)) {
-                    linkFiles(fromFiles, toDir);
+                    createDirsAndLinkFiles(fromFiles, toDir, mInheritedFilesBase);
                 } else {
                     // TODO: this should delegate to DCS so the system process
                     // avoids holding open FDs into containers.
@@ -690,6 +699,31 @@
                     }
                 }
             }
+
+            // Inherit compiled oat directory.
+            final File packageInstallDir = (new File(app.getBaseCodePath())).getParentFile();
+            mInheritedFilesBase = packageInstallDir;
+            final File oatDir = new File(packageInstallDir, "oat");
+            if (oatDir.exists()) {
+                final File[] archSubdirs = oatDir.listFiles();
+                // Only add "oatDir" if it contains arch specific subdirs.
+                if (archSubdirs != null && archSubdirs.length > 0) {
+                    mResolvedInheritedFiles.add(oatDir);
+                }
+                final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
+                for (File archSubDir : archSubdirs) {
+                    // Skip any directory that isn't an ISA subdir.
+                    if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
+                        continue;
+                    }
+
+                    List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
+                    if (!oatFiles.isEmpty()) {
+                        mResolvedInheritedFiles.add(archSubDir);
+                        mResolvedInheritedFiles.addAll(oatFiles);
+                    }
+                }
+            }
         }
     }
 
@@ -768,14 +802,69 @@
         return true;
     }
 
-    private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
+    /**
+     * Reparents the path of {@code file} from {@code oldBase} to {@code newBase}. {@code file}
+     * must necessarily be a subpath of {@code oldBase}. It is an error for {@code file} to have
+     * relative path components such as {@code "."} or {@code ".."}. For example, for we will
+     * reparent {@code /foo/bar/baz} to {@code /foo2/bar/baz} if {@code oldBase} was {@code /foo}
+     * and {@code newBase} was {@code /foo2}.
+     */
+    private static File reparentPath(File file, File oldBase, File newBase) throws IOException {
+        final String oldBaseStr = oldBase.getAbsolutePath();
+        final String pathStr = file.getAbsolutePath();
+
+        // Don't allow relative paths.
+        if (pathStr.contains("/.") ) {
+            throw new IOException("Invalid path (was relative) : " + pathStr);
+        }
+
+        if (pathStr.startsWith(oldBaseStr)) {
+            final String relative = pathStr.substring(oldBaseStr.length());
+            return new File(newBase, relative);
+        }
+
+        throw new IOException("File: " + pathStr + " outside base: " + oldBaseStr);
+    }
+
+    /**
+     * Recreates a directory and file structure, specified by a list of files {@code fromFiles}
+     * which are subpaths of {@code fromDir} to {@code toDir}. Directories are created with the
+     * same permissions, and regular files are linked.
+     *
+     * TODO: Move this function to installd so that the system process doesn't have to
+     * manipulate / relabel directories.
+     */
+    private static void createDirsAndLinkFiles(List<File> fromFiles, File toDir, File fromDir)
+            throws IOException {
         for (File fromFile : fromFiles) {
-            final File toFile = new File(toDir, fromFile.getName());
+            final File toFile = reparentPath(fromFile, fromDir, toDir);
+            final StructStat stat;
             try {
-                if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
-                Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+                stat = Os.stat(fromFile.getAbsolutePath());
             } catch (ErrnoException e) {
-                throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+                throw new IOException("Failed to stat: " + fromFile.getAbsolutePath(), e);
+            }
+
+            if (OsConstants.S_ISDIR(stat.st_mode)) {
+                if (LOGD) Slog.d(TAG, "Creating directory " + toFile.getAbsolutePath());
+                try {
+                    Os.mkdir(toFile.getAbsolutePath(), stat.st_mode);
+                } catch (ErrnoException e) {
+                    throw new IOException("Failed to create dir: " + toFile.getAbsolutePath(), e);
+                }
+
+                // We do this to ensure that the oat/ directory is created with the right
+                // label (data_dalvikcache_file) instead of apk_tmpfile.
+                if (!SELinux.restorecon(toFile)) {
+                    throw new IOException("Failed to restorecon: " + toFile.getAbsolutePath());
+                }
+            } else {
+                if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
+                try {
+                    Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+                } catch (ErrnoException e) {
+                    throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+                }
             }
         }
         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 47e3963..e370afc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1876,7 +1876,7 @@
             alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
 
             /**
-             * And there are a number of commands implemented in Java, which
+             * There are a number of commands implemented in Java, which
              * we currently need to do the dexopt on so that they can be
              * run from a non-root shell.
              */
@@ -6361,63 +6361,39 @@
 
         final String path = scanFile.getPath();
         final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
-        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
-            setBundledAppAbisAndRoots(pkg, pkgSetting);
 
-            // If we haven't found any native libraries for the app, check if it has
-            // renderscript code. We'll need to force the app to 32 bit if it has
-            // renderscript bitcode.
-            if (pkg.applicationInfo.primaryCpuAbi == null
-                    && pkg.applicationInfo.secondaryCpuAbi == null
-                    && Build.SUPPORTED_64_BIT_ABIS.length >  0) {
-                NativeLibraryHelper.Handle handle = null;
-                try {
-                    handle = NativeLibraryHelper.Handle.create(scanFile);
-                    if (NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
-                        pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
-                    }
-                } catch (IOException ioe) {
-                    Slog.w(TAG, "Error scanning system app : " + ioe);
-                } finally {
-                    IoUtils.closeQuietly(handle);
-                }
-            }
-
-            setNativeLibraryPaths(pkg);
+        if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+            derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
         } else {
-            if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
-                deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
-            } else {
-                if ((scanFlags & SCAN_MOVE) != 0) {
-                    // We haven't run dex-opt for this move (since we've moved the compiled output too)
-                    // but we already have this packages package info in the PackageSetting. We just
-                    // use that and derive the native library path based on the new codepath.
-                    pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
-                    pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
-                }
-
-                // Set native library paths again. For moves, the path will be updated based on the
-                // ABIs we've determined above. For non-moves, the path will be updated based on the
-                // ABIs we determined during compilation, but the path will depend on the final
-                // package path (after the rename away from the stage path).
-                setNativeLibraryPaths(pkg);
+            if ((scanFlags & SCAN_MOVE) != 0) {
+                // We haven't run dex-opt for this move (since we've moved the compiled output too)
+                // but we already have this packages package info in the PackageSetting. We just
+                // use that and derive the native library path based on the new codepath.
+                pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
+                pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
             }
 
-            if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
-            final int[] userIds = sUserManager.getUserIds();
-            synchronized (mInstallLock) {
-                // Create a native library symlink only if we have native libraries
-                // and if the native libraries are 32 bit libraries. We do not provide
-                // this symlink for 64 bit libraries.
-                if (pkg.applicationInfo.primaryCpuAbi != null &&
-                        !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
-                    final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
-                    for (int userId : userIds) {
-                        if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
-                                nativeLibPath, userId) < 0) {
-                            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                                    "Failed linking native library dir (user=" + userId + ")");
-                        }
+            // Set native library paths again. For moves, the path will be updated based on the
+            // ABIs we've determined above. For non-moves, the path will be updated based on the
+            // ABIs we determined during compilation, but the path will depend on the final
+            // package path (after the rename away from the stage path).
+            setNativeLibraryPaths(pkg);
+        }
+
+        if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
+        final int[] userIds = sUserManager.getUserIds();
+        synchronized (mInstallLock) {
+            // Create a native library symlink only if we have native libraries
+            // and if the native libraries are 32 bit libraries. We do not provide
+            // this symlink for 64 bit libraries.
+            if (pkg.applicationInfo.primaryCpuAbi != null &&
+                    !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
+                final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
+                for (int userId : userIds) {
+                    if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+                            nativeLibPath, userId) < 0) {
+                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                                "Failed linking native library dir (user=" + userId + ")");
                     }
                 }
             }
@@ -6946,8 +6922,8 @@
      *
      * If {@code extractLibs} is true, native libraries are extracted from the app if required.
      */
-    public void deriveNonSystemPackageAbi(PackageParser.Package pkg, File scanFile,
-                                          String cpuAbiOverride, boolean extractLibs)
+    public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
+                                 String cpuAbiOverride, boolean extractLibs)
             throws PackageManagerException {
         // TODO: We can probably be smarter about this stuff. For installed apps,
         // we can calculate this information at install time once and for all. For
@@ -6959,8 +6935,10 @@
         setNativeLibraryPaths(pkg);
 
         // We would never need to extract libs for forward-locked and external packages,
-        // since the container service will do it for us.
-        if (pkg.isForwardLocked() || isExternal(pkg)) {
+        // since the container service will do it for us. We shouldn't attempt to
+        // extract libs from system app when it was not updated.
+        if (pkg.isForwardLocked() || isExternal(pkg) ||
+            (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) {
             extractLibs = false;
         }
 
@@ -7291,33 +7269,6 @@
     }
 
     /**
-     * Calculate the abis and roots for a bundled app. These can uniquely
-     * be determined from the contents of the system partition, i.e whether
-     * it contains 64 or 32 bit shared libraries etc. We do not validate any
-     * of this information, and instead assume that the system was built
-     * sensibly.
-     */
-    private void setBundledAppAbisAndRoots(PackageParser.Package pkg,
-                                           PackageSetting pkgSetting) {
-        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
-
-        // If "/system/lib64/apkname" exists, assume that is the per-package
-        // native library directory to use; otherwise use "/system/lib/apkname".
-        final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
-        setBundledAppAbi(pkg, apkRoot, apkName);
-        // pkgSetting might be null during rescan following uninstall of updates
-        // to a bundled app, so accommodate that possibility.  The settings in
-        // that case will be established later from the parsed package.
-        //
-        // If the settings aren't null, sync them up with what we've just derived.
-        // note that apkRoot isn't stored in the package settings.
-        if (pkgSetting != null) {
-            pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
-            pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
-        }
-    }
-
-    /**
      * Deduces the ABI of a bundled app and sets the relevant fields on the
      * parsed pkg object.
      *
@@ -11504,7 +11455,7 @@
         final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
                 || (args.volumeUuid != null));
         boolean replace = false;
-        int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
+        int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
         // Result object to be returned
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
 
@@ -11695,7 +11646,7 @@
             scanFlags |= SCAN_NO_DEX;
 
             try {
-                deriveNonSystemPackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
+                derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
                         true /* extract libs */);
             } catch (PackageManagerException pme) {
                 Slog.e(TAG, "Error deriving application ABI", pme);
@@ -11705,7 +11656,7 @@
 
             // Run dexopt before old package gets removed, to minimize time when app is unavailable
             int result = mPackageDexOptimizer
-                    .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
+                    .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
                             false /* defer */, false /* inclDependencies */);
             if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                 res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b8d0692..46793b9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1087,16 +1087,19 @@
         }
     }
 
-    private void sleepPress(KeyEvent event) {
+    private void sleepPress(long eventTime) {
+        if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
+            launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
+        }
+    }
+
+    private void sleepRelease(long eventTime) {
         switch (mShortPressOnSleepBehavior) {
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
-                mPowerManager.goToSleep(event.getEventTime(),
-                        PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
-                break;
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
-                launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
-                mPowerManager.goToSleep(event.getEventTime(),
-                        PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+                Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
+                mPowerManager.goToSleep(eventTime,
+                       PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
         }
     }
@@ -2116,7 +2119,9 @@
             case TYPE_KEYGUARD_SCRIM:
                 return false;
             default:
-                return true;
+                // Hide only windows below the keyguard host window.
+                return windowTypeToLayerLw(win.getBaseType())
+                        < windowTypeToLayerLw(TYPE_STATUS_BAR);
         }
     }
 
@@ -4903,7 +4908,11 @@
                 if (!mPowerManager.isInteractive()) {
                     useHapticFeedback = false; // suppress feedback if already non-interactive
                 }
-                sleepPress(event);
+                if (down) {
+                    sleepPress(event.getEventTime());
+                } else {
+                    sleepRelease(event.getEventTime());
+                }
                 break;
             }
 
@@ -5517,6 +5526,12 @@
 
     /** {@inheritDoc} */
     @Override
+    public boolean isKeyguardShowingOrOccluded() {
+        return mKeyguardDelegate == null ? false : mKeyguardDelegate.isShowing();
+    }
+
+    /** {@inheritDoc} */
+    @Override
     public boolean inKeyguardRestrictedKeyInputMode() {
         if (mKeyguardDelegate == null) return false;
         return mKeyguardDelegate.isInputRestricted();
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 01c110f..f1f9c50 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -36,10 +36,11 @@
     // Keyguard changes its state, it always triggers a layout in window manager. Because
     // IKeyguardStateCallback is synchronous and because these states are declared volatile, it's
     // guaranteed that window manager picks up the new state all the time in the layout caused by
-    // the state change of Keyguard.
-    private volatile boolean mIsShowing;
-    private volatile boolean mSimSecure;
-    private volatile boolean mInputRestricted;
+    // the state change of Keyguard. To be extra safe, assume most restrictive values until Keyguard
+    // tells us the actual value.
+    private volatile boolean mIsShowing = true;
+    private volatile boolean mSimSecure = true;
+    private volatile boolean mInputRestricted = true;
 
     private int mCurrentUserId;
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 9169351..3305e1e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -191,7 +191,8 @@
     private boolean shouldForceHide(WindowState win) {
         final WindowState imeTarget = mService.mInputMethodTarget;
         final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() &&
-                (imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+                ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
+                        || !mPolicy.canBeForceHidden(imeTarget, imeTarget.mAttrs));
 
         final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
         final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
@@ -203,7 +204,11 @@
                         || (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.isAnimatingLw()
                         // Show error dialogs over apps that dismiss keyguard.
                         || (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0)));
-        return (mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked;
+
+        // Only hide windows if the keyguard is active and not animating away.
+        boolean keyguardOn = mPolicy.isKeyguardShowingOrOccluded()
+                && mForceHiding != KEYGUARD_ANIMATING_OUT;
+        return keyguardOn && hideWhenLocked;
     }
 
     private void updateWindowsLocked(final int displayId) {
@@ -228,6 +233,7 @@
                         winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
                         winAnimator.mAnimationIsEntrance = false;
                         winAnimator.mAnimationStartTime = -1;
+                        winAnimator.mKeyguardGoingAwayAnimation = true;
                     }
                 } else {
                     if (DEBUG_KEYGUARD) Slog.d(TAG,
@@ -310,7 +316,7 @@
                         mKeyguardGoingAway = false;
                     }
                     if (win.isReadyForDisplay()) {
-                        if (nowAnimating) {
+                        if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
                             mForceHiding = KEYGUARD_ANIMATING_OUT;
                         } else {
                             mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7019453..6b5c224 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2734,14 +2734,16 @@
                 }
             }
             final AppWindowToken appToken = win.mAppToken;
+            // Prevent an immediate window exit only for a real animation, ignoring e.g.
+            // dummy animations.
+            final boolean inAnimation = win.mWinAnimator.isWindowAnimatingNow();
             // The starting window is the last window in this app token and it isn't animating.
             // Allow it to be removed now as there is no additional window or animation that will
             // trigger its removal.
             final boolean lastWinStartingNotAnimating = startingWindow && appToken!= null
-                    && appToken.allAppWindows.size() == 1 && !win.mWinAnimator.isWindowAnimating();
-            if (!lastWinStartingNotAnimating && (win.mExiting || win.mWinAnimator.isAnimating())) {
+                    && appToken.allAppWindows.size() == 1 && !inAnimation;
+            if (!lastWinStartingNotAnimating && (win.mExiting || inAnimation)) {
                 // The exit animation is running... wait for it!
-                //Slog.i(TAG, "*** Running exit animation...");
                 win.mExiting = true;
                 win.mRemoveOnExit = true;
                 final DisplayContent displayContent = win.getDisplayContent();
@@ -2757,7 +2759,6 @@
                 if (focusChanged) {
                     mInputMonitor.updateInputWindowsLw(false /*force*/);
                 }
-                //dump();
                 Binder.restoreCallingIdentity(origId);
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e9023fd..b42c8eb 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -251,11 +251,21 @@
                 && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
     }
 
-    /** Is this window currently animating? */
+    /** Is this window currently set to animate or currently animating?
+     *  NOTE: The method will return true for cases where the window isn't currently animating, but
+     *  is set to animate. i.e. if the window animation is currently set to a dummy placeholder
+     *  animation. Use {@link #isWindowAnimatingNow} to know if the window is currently running a
+     *  real animation. */
     boolean isWindowAnimating() {
         return mAnimation != null;
     }
 
+    /** Is the window performing a real animation and not a dummy which is only waiting for an
+     * an animation to start? */
+    boolean isWindowAnimatingNow() {
+        return isWindowAnimating() && !isDummyAnimation();
+    }
+
     void cancelExitAnimationForNextAnimationLocked() {
         if (mAnimation != null) {
             mAnimation.cancel();
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index c0e1d19..334f708 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -30,8 +30,8 @@
     private final Inet4Address mSrcIp;
 
     DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
-                  Inet4Address clientIp, byte[] clientMac) {
-        super(transId, secs, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
+                  Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+        super(transId, secs, clientIp, yourIp, serverAddress, INADDR_ANY, clientMac, broadcast);
         mBroadcast = broadcast;
         mSrcIp = serverAddress;
     }
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index d8d74e2..575a300 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -58,7 +58,7 @@
 import java.util.Arrays;
 import java.util.Random;
 
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 
 import static android.system.OsConstants.*;
 import static android.net.dhcp.DhcpPacket.*;
@@ -297,9 +297,15 @@
         return true;
     }
 
+    private static void closeQuietly(FileDescriptor fd) {
+        try {
+            IoBridge.closeAndSignalBlockedThreads(fd);
+        } catch (IOException ignored) {}
+    }
+
     private void closeSockets() {
-        IoUtils.closeQuietly(mUdpSock);
-        IoUtils.closeQuietly(mPacketSock);
+        closeQuietly(mUdpSock);
+        closeQuietly(mPacketSock);
     }
 
     private boolean setIpAddress(LinkAddress address) {
@@ -326,7 +332,7 @@
 
         @Override
         public void run() {
-            maybeLog("Starting receive thread");
+            maybeLog("Receive thread started");
             while (!stopped) {
                 try {
                     int length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
@@ -345,7 +351,7 @@
                     }
                 }
             }
-            maybeLog("Stopping receive thread");
+            maybeLog("Receive thread stopped");
         }
     }
 
@@ -463,6 +469,8 @@
                     return "CMD_KICK";
                 case CMD_RECEIVED_PACKET:
                     return "CMD_RECEIVED_PACKET";
+                case CMD_TIMEOUT:
+                    return "CMD_TIMEOUT";
                 default:
                     return Integer.toString(what);
             }
@@ -601,12 +609,15 @@
 
     /**
      * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
-     * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm.
+     * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
+     * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
+     * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
+     * state.
      *
      * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
      * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
-     * sent by the receive thread. They may implement timeout, which is called when the timeout
-     * fires.
+     * sent by the receive thread. They may also set mTimeout and if desired override the default
+     * timeout implementation.
      */
     abstract class PacketRetransmittingState extends LoggingState {
 
@@ -647,7 +658,19 @@
 
         abstract protected boolean sendPacket();
         abstract protected void receivePacket(DhcpPacket packet);
-        protected void timeout() {}
+
+        // Default implementation of timeout. This is only invoked if mTimeout > 0, so it will never
+        // be called if the subclass does not set a timeout.
+        protected void timeout() {
+            maybeLog("Timeout in " + getName());
+            notifyFailure();
+            if (this != mDhcpInitState) {
+                // Only transition to INIT if we're not already there. Otherwise, we'll exit the
+                // state and re-enter it, which will reset the packet transmission interval, re-set
+                // the timeout, etc.
+                transitionTo(mDhcpInitState);
+            }
+        }
 
         protected void initTimer() {
             mTimer = FIRST_TIMEOUT_MS;
@@ -696,11 +719,6 @@
             return sendDiscoverPacket();
         }
 
-        protected void timeout() {
-            maybeLog("Timeout");
-            notifyFailure();
-        }
-
         protected void receivePacket(DhcpPacket packet) {
             if (!isValidPacket(packet)) return;
             if (!(packet instanceof DhcpOfferPacket)) return;
@@ -747,11 +765,6 @@
                 transitionTo(mDhcpInitState);
             }
         }
-
-        protected void timeout() {
-            notifyFailure();
-            transitionTo(mDhcpInitState);
-        }
     }
 
     class DhcpHaveAddressState extends LoggingState {
@@ -760,6 +773,8 @@
             super.enter();
             if (!setIpAddress(mDhcpLease.ipAddress)) {
                 notifyFailure();
+                // There's likely no point in going into DhcpInitState here, we'll probably just
+                // repeat the transaction, get the same IP address as before, and fail.
                 transitionTo(mStoppedState);
             }
         }
@@ -797,7 +812,6 @@
         }
     }
 
-    // TODO: timeout.
     class DhcpRenewingState extends PacketRetransmittingState {
         public DhcpRenewingState() {
             super();
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index af41708..7ca7100 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -32,8 +32,8 @@
      * Generates a OFFER packet with the specified parameters.
      */
     DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
-                    Inet4Address clientIp, byte[] clientMac) {
-        super(transId, secs, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+                    Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+        super(transId, secs, clientIp, yourIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
         mSrcIp = serverAddress;
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 2a25d30..d42404b 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -919,7 +919,7 @@
                 break;
             case DHCP_MESSAGE_TYPE_OFFER:
                 newPacket = new DhcpOfferPacket(
-                    transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+                    transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_REQUEST:
                 newPacket = new DhcpRequestPacket(
@@ -932,7 +932,7 @@
                 break;
             case DHCP_MESSAGE_TYPE_ACK:
                 newPacket = new DhcpAckPacket(
-                    transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+                    transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_NAK:
                 newPacket = new DhcpNakPacket(
@@ -982,9 +982,9 @@
      */
     public DhcpResults toDhcpResults() {
         Inet4Address ipAddress = mYourIp;
-        if (ipAddress == Inet4Address.ANY) {
+        if (ipAddress.equals(Inet4Address.ANY)) {
             ipAddress = mClientIp;
-            if (ipAddress == Inet4Address.ANY) {
+            if (ipAddress.equals(Inet4Address.ANY)) {
                 return null;
             }
         }
@@ -1052,7 +1052,7 @@
         Inet4Address gateway, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpOfferPacket(
-            transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+            transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
         pkt.mGateway = gateway;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
@@ -1072,7 +1072,7 @@
         Inet4Address gateway, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpAckPacket(
-            transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+            transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
         pkt.mGateway = gateway;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 636dd4d..919293a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -36,6 +36,9 @@
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index f116042..e0e3fcf 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -18,6 +18,7 @@
 
 import android.net.NetworkUtils;
 import android.net.DhcpResults;
+import android.net.LinkAddress;
 import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
 import junit.framework.TestCase;
@@ -34,19 +35,27 @@
             (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
     private static Inet4Address CLIENT_ADDR =
             (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
+    // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
+    // doesn't use == instead of equals when comparing addresses.
+    private static Inet4Address ANY = (Inet4Address) NetworkUtils.numericToInetAddress("0.0.0.0");
+
     private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
 
     class TestDhcpPacket extends DhcpPacket {
         private byte mType;
         // TODO: Make this a map of option numbers to bytes instead.
-        private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes;
+        private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
 
-        public TestDhcpPacket(byte type) {
-            super(0xdeadbeef, (short) 0, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY,
+        public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
+            super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
                   CLIENT_MAC, true);
             mType = type;
         }
 
+        public TestDhcpPacket(byte type) {
+            this(type, INADDR_ANY, CLIENT_ADDR);
+        }
+
         public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
             mDomainBytes = domainBytes;
             return this;
@@ -62,6 +71,11 @@
             return this;
         }
 
+        public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
+            mNetmaskBytes = netmaskBytes;
+            return this;
+        }
+
         public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
             ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
             fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
@@ -80,6 +94,9 @@
             if (mLeaseTimeBytes != null) {
                 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
             }
+            if (mNetmaskBytes != null) {
+                addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
+            }
             addTlvEnd(buffer);
         }
 
@@ -175,4 +192,50 @@
         assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
         assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
     }
+
+    private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
+                                byte[] netmaskBytes) {
+        checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
+        checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
+    }
+
+    private void checkIpAddress(String expected, byte type,
+                                Inet4Address clientIp, Inet4Address yourIp,
+                                byte[] netmaskBytes) {
+        ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
+                .setNetmaskBytes(netmaskBytes)
+                .build();
+        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+        DhcpResults results = offerPacket.toDhcpResults();
+
+        if (expected != null) {
+            LinkAddress expectedAddress = new LinkAddress(expected);
+            assertEquals(expectedAddress, results.ipAddress);
+        } else {
+            assertNull(results);
+        }
+    }
+
+    @SmallTest
+    public void testIpAddress() throws Exception {
+        byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
+        byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
+        byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
+        Inet4Address example1 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
+        Inet4Address example2 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.43");
+
+        // A packet without any addresses is not valid.
+        checkIpAddress(null, ANY, ANY, slash24Netmask);
+
+        // ClientIP is used iff YourIP is not present.
+        checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
+        checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
+        checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
+
+        // Invalid netmasks are ignored.
+        checkIpAddress(null, example2, ANY, invalidNetmask);
+
+        // If there is no netmask, implicit netmasks are used.
+        checkIpAddress("192.0.2.43/24", ANY, example2, null);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 48d8ffb..56f1d48c 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,10 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.getNetworkTypeName;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isA;
@@ -30,21 +34,36 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkMisc;
+import android.net.NetworkRequest;
 import android.net.RouteInfo;
+import android.os.ConditionVariable;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.INetworkManagementService;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 import android.util.LogPrinter;
 
+import com.android.server.connectivity.NetworkMonitor;
+
 import org.mockito.ArgumentCaptor;
 
 import java.net.InetAddress;
@@ -52,8 +71,10 @@
 
 /**
  * Tests for {@link ConnectivityService}.
+ *
+ * Build, install and run with:
+ *  runtest frameworks-services -c com.android.server.ConnectivityServiceTest
  */
-@LargeTest
 public class ConnectivityServiceTest extends AndroidTestCase {
     private static final String TAG = "ConnectivityServiceTest";
 
@@ -75,73 +96,336 @@
     private INetworkManagementService mNetManager;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyService;
-//    private ConnectivityService.NetworkFactory mNetFactory;
 
     private BroadcastInterceptingContext mServiceContext;
     private ConnectivityService mService;
+    private ConnectivityManager mCm;
+    private MockNetworkAgent mWiFiNetworkAgent;
+    private MockNetworkAgent mCellNetworkAgent;
 
-// TODO: rework with network factory
-//    private MockNetwork mMobile;
-//    private MockNetwork mWifi;
-//
-//    private Handler mTrackerHandler;
-//
-//    private static class MockNetwork {
-//        public NetworkStateTracker tracker;
-//        public NetworkInfo info;
-//        public LinkProperties link;
-//
-//        public MockNetwork(int type) {
-//            tracker = mock(NetworkStateTracker.class);
-//            info = new NetworkInfo(type, -1, getNetworkTypeName(type), null);
-//            link = new LinkProperties();
-//        }
-//
-//        public void doReturnDefaults() {
-//            // TODO: eventually CS should make defensive copies
-//            doReturn(new NetworkInfo(info)).when(tracker).getNetworkInfo();
-//            doReturn(new LinkProperties(link)).when(tracker).getLinkProperties();
-//
-//            // fallback to default TCP buffers
-//            doReturn("").when(tracker).getTcpBufferSizesPropName();
-//        }
-//    }
-//
-//    @Override
-//    public void setUp() throws Exception {
-//        super.setUp();
-//
-//        mServiceContext = new BroadcastInterceptingContext(getContext());
-//
-//        mNetManager = mock(INetworkManagementService.class);
-//        mStatsService = mock(INetworkStatsService.class);
-//        mPolicyService = mock(INetworkPolicyManager.class);
-//        mNetFactory = mock(ConnectivityService.NetworkFactory.class);
-//
-//        mMobile = new MockNetwork(TYPE_MOBILE);
-//        mWifi = new MockNetwork(TYPE_WIFI);
-//
-//        // omit most network trackers
-//        doThrow(new IllegalArgumentException("Not supported in test environment"))
-//                .when(mNetFactory).createTracker(anyInt(), isA(NetworkConfig.class));
-//
-//        doReturn(mMobile.tracker)
-//                .when(mNetFactory).createTracker(eq(TYPE_MOBILE), isA(NetworkConfig.class));
-//        doReturn(mWifi.tracker)
-//                .when(mNetFactory).createTracker(eq(TYPE_WIFI), isA(NetworkConfig.class));
-//
-//        final ArgumentCaptor<Handler> trackerHandler = ArgumentCaptor.forClass(Handler.class);
-//        doNothing().when(mMobile.tracker)
-//                .startMonitoring(isA(Context.class), trackerHandler.capture());
-//
-//        mService = new ConnectivityService(
-//                mServiceContext, mNetManager, mStatsService, mPolicyService);
-//        mService.systemReady();
-//
-//        mTrackerHandler = trackerHandler.getValue();
-//        mTrackerHandler.getLooper().setMessageLogging(new LogPrinter(Log.INFO, TAG));
-//    }
-//
+    private class MockContext extends BroadcastInterceptingContext {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+            // PendingIntents sent by the AlarmManager are not intercepted by
+            // BroadcastInterceptingContext so we must really register the receiver.
+            // This shouldn't effect the real NetworkMonitors as the action contains a random token.
+            if (filter.getAction(0).startsWith("android.net.netmon.lingerExpired")) {
+                return getBaseContext().registerReceiver(receiver, filter);
+            } else {
+                return super.registerReceiver(receiver, filter);
+            }
+        }
+
+        @Override
+        public Object getSystemService (String name) {
+            if (name == Context.CONNECTIVITY_SERVICE) return mCm;
+            return super.getSystemService(name);
+        }
+    }
+
+    private class MockNetworkAgent {
+        private final NetworkInfo mNetworkInfo;
+        private final NetworkCapabilities mNetworkCapabilities;
+        private final Thread mThread;
+        private NetworkAgent mNetworkAgent;
+
+        MockNetworkAgent(int transport) {
+            final int type = transportToLegacyType(transport);
+            final String typeName = ConnectivityManager.getNetworkTypeName(type);
+            mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
+            mNetworkCapabilities = new NetworkCapabilities();
+            mNetworkCapabilities.addTransportType(transport);
+            final int score;
+            switch (transport) {
+                case TRANSPORT_WIFI:
+                    score = 60;
+                    break;
+                case TRANSPORT_CELLULAR:
+                    score = 50;
+                    break;
+                default:
+                    throw new UnsupportedOperationException("unimplemented network type");
+            }
+            final ConditionVariable initComplete = new ConditionVariable();
+            mThread = new Thread() {
+                public void run() {
+                    Looper.prepare();
+                    mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext,
+                            "Mock" + typeName, mNetworkInfo, mNetworkCapabilities,
+                            new LinkProperties(), score, new NetworkMisc()) {
+                        public void unwanted() {}
+                    };
+                    initComplete.open();
+                    Looper.loop();
+                }
+            };
+            mThread.start();
+            initComplete.block();
+        }
+
+        /**
+         * Transition this NetworkAgent to CONNECTED state.
+         * @param validated Indicate if network should pretend to be validated.
+         */
+        public void connect(boolean validated) {
+            assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
+            assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
+
+            // To pretend network is validated, we transition it to the CONNECTED state without
+            // NET_CAPABILITY_INTERNET so NetworkMonitor doesn't bother trying to validate and
+            // just rubber stamps it as validated.  Afterwards we add NET_CAPABILITY_INTERNET so
+            // the network can satisfy the default request.
+            NetworkCallback callback = null;
+            final ConditionVariable validatedCv = new ConditionVariable();
+            if (validated) {
+                // If we connect a network without INTERNET capability, it'll get reaped.
+                // Prevent the reaping by adding a NetworkRequest.
+                NetworkRequest request = new NetworkRequest.Builder()
+                        .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
+                        .build();
+                callback = new NetworkCallback() {
+                    public void onCapabilitiesChanged(Network network,
+                            NetworkCapabilities networkCapabilities) {
+                        if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                            validatedCv.open();
+                        }
+                    }
+                };
+                mCm.requestNetwork(request, callback);
+            } else {
+                mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+                mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+            }
+
+            mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+
+            if (validated) {
+                // Wait for network to validate.
+                validatedCv.block();
+                mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+                mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+            }
+
+            if (callback != null) mCm.unregisterNetworkCallback(callback);
+        }
+
+        public void disconnect() {
+            mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        }
+
+        public Network getNetwork() {
+            return new Network(mNetworkAgent.netId);
+        }
+    }
+
+    private class WrappedConnectivityService extends ConnectivityService {
+        public WrappedConnectivityService(Context context, INetworkManagementService netManager,
+                INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+            super(context, netManager, statsService, policyManager);
+        }
+
+        @Override
+        protected int getDefaultTcpRwnd() {
+            // Prevent wrapped ConnectivityService from trying to write to SystemProperties.
+            return 0;
+        }
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mServiceContext = new MockContext(getContext());
+
+        mNetManager = mock(INetworkManagementService.class);
+        mStatsService = mock(INetworkStatsService.class);
+        mPolicyService = mock(INetworkPolicyManager.class);
+
+        mService = new WrappedConnectivityService(
+                mServiceContext, mNetManager, mStatsService, mPolicyService);
+        mService.systemReady();
+        mCm = new ConnectivityManager(mService);
+    }
+
+    private int transportToLegacyType(int transport) {
+        switch (transport) {
+            case TRANSPORT_WIFI:
+                return TYPE_WIFI;
+            case TRANSPORT_CELLULAR:
+                return TYPE_MOBILE;
+            default:
+                throw new IllegalStateException("Unknown transport" + transport);
+        }
+    }
+
+    private void verifyActiveNetwork(int transport) {
+        // Test getActiveNetworkInfo()
+        assertNotNull(mCm.getActiveNetworkInfo());
+        assertEquals(transportToLegacyType(transport), mCm.getActiveNetworkInfo().getType());
+        // Test getActiveNetwork()
+        assertNotNull(mCm.getActiveNetwork());
+        switch (transport) {
+            case TRANSPORT_WIFI:
+                assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
+                break;
+            case TRANSPORT_CELLULAR:
+                assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork());
+                break;
+            default:
+                throw new IllegalStateException("Unknown transport" + transport);
+        }
+        // Test getNetworkInfo(Network)
+        assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork()));
+        assertEquals(transportToLegacyType(transport), mCm.getNetworkInfo(mCm.getActiveNetwork()).getType());
+        // Test getNetworkCapabilities(Network)
+        assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork()));
+        assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport));
+    }
+
+    private void verifyNoNetwork() {
+        // Test getActiveNetworkInfo()
+        assertNull(mCm.getActiveNetworkInfo());
+        // Test getActiveNetwork()
+        assertNull(mCm.getActiveNetwork());
+        // Test getAllNetworks()
+        assertEquals(0, mCm.getAllNetworks().length);
+    }
+
+    /**
+     * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
+     * broadcasts are received.
+     */
+    private ConditionVariable waitForConnectivityBroadcasts(final int count) {
+        final ConditionVariable cv = new ConditionVariable();
+        mServiceContext.registerReceiver(new BroadcastReceiver() {
+                    private int remaining = count;
+                    public void onReceive(Context context, Intent intent) {
+                        if (--remaining == 0) {
+                            cv.open();
+                            mServiceContext.unregisterReceiver(this);
+                        }
+                    }
+                }, new IntentFilter(CONNECTIVITY_ACTION));
+        return cv;
+    }
+
+    @LargeTest
+    public void testLingering() throws Exception {
+        // Decrease linger timeout to the minimum allowed by AlarmManagerService.
+        NetworkMonitor.SetDefaultLingerTime(5000);
+        verifyNoNetwork();
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        assertNull(mCm.getActiveNetworkInfo());
+        assertNull(mCm.getActiveNetwork());
+        // Test bringing up validated cellular.
+        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        mCellNetworkAgent.connect(true);
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_CELLULAR);
+        assertEquals(2, mCm.getAllNetworks().length);
+        assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
+                mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
+        assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
+                mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
+        // Test bringing up validated WiFi.
+        cv = waitForConnectivityBroadcasts(2);
+        mWiFiNetworkAgent.connect(true);
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        assertEquals(2, mCm.getAllNetworks().length);
+        assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
+                mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
+        assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
+                mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
+        // Test cellular linger timeout.
+        try {
+            Thread.sleep(6000);
+        } catch (Exception e) {
+        }
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        assertEquals(1, mCm.getAllNetworks().length);
+        assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
+        // Test WiFi disconnect.
+        cv = waitForConnectivityBroadcasts(1);
+        mWiFiNetworkAgent.disconnect();
+        cv.block();
+        verifyNoNetwork();
+    }
+
+    @LargeTest
+    public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
+        // Test bringing up unvalidated WiFi
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        mWiFiNetworkAgent.connect(false);
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        // Test bringing up unvalidated cellular
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(false);
+        try {
+            Thread.sleep(1000);
+        } catch (Exception e) {
+        }
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        // Test cellular disconnect.
+        mCellNetworkAgent.disconnect();
+        try {
+            Thread.sleep(1000);
+        } catch (Exception e) {
+        }
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        // Test bringing up validated cellular
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        cv = waitForConnectivityBroadcasts(2);
+        mCellNetworkAgent.connect(true);
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_CELLULAR);
+        // Test cellular disconnect.
+        cv = waitForConnectivityBroadcasts(2);
+        mCellNetworkAgent.disconnect();
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        // Test WiFi disconnect.
+        cv = waitForConnectivityBroadcasts(1);
+        mWiFiNetworkAgent.disconnect();
+        cv.block();
+        verifyNoNetwork();
+    }
+
+    @LargeTest
+    public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
+        // Test bringing up unvalidated cellular.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        mCellNetworkAgent.connect(false);
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_CELLULAR);
+        // Test bringing up unvalidated WiFi.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        cv = waitForConnectivityBroadcasts(2);
+        mWiFiNetworkAgent.connect(false);
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        // Test WiFi disconnect.
+        cv = waitForConnectivityBroadcasts(2);
+        mWiFiNetworkAgent.disconnect();
+        cv.block();
+        verifyActiveNetwork(TRANSPORT_CELLULAR);
+        // Test cellular disconnect.
+        cv = waitForConnectivityBroadcasts(1);
+        mCellNetworkAgent.disconnect();
+        cv.block();
+        verifyNoNetwork();
+    }
+
 //    @Override
 //    public void tearDown() throws Exception {
 //        super.tearDown();
@@ -157,9 +441,9 @@
 //        mMobile.link.addRoute(MOBILE_ROUTE_V6);
 //        mMobile.doReturnDefaults();
 //
-//        nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+//        cv = waitForConnectivityBroadcasts(1);
 //        mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-//        nextConnBroadcast.get();
+//        cv.block();
 //
 //        // verify that both routes were added
 //        int mobileNetId = mMobile.tracker.getNetwork().netId;
@@ -177,9 +461,9 @@
 //        mMobile.link.addRoute(MOBILE_ROUTE_V6);
 //        mMobile.doReturnDefaults();
 //
-//        nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+//        cv = waitForConnectivityBroadcasts(1);
 //        mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-//        nextConnBroadcast.get();
+//        cv.block();
 //
 //        reset(mNetManager);
 //
@@ -193,9 +477,9 @@
 //        // expect that mobile will be torn down
 //        doReturn(true).when(mMobile.tracker).teardown();
 //
-//        nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+//        cv = waitForConnectivityBroadcasts(1);
 //        mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget();
-//        nextConnBroadcast.get();
+//        cv.block();
 //
 //        // verify that wifi routes added, and teardown requested
 //        int wifiNetId = mWifi.tracker.getNetwork().netId;
@@ -212,9 +496,9 @@
 //        mMobile.link.clear();
 //        mMobile.doReturnDefaults();
 //
-//        nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+//        cv = waitForConnectivityBroadcasts(1);
 //        mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-//        nextConnBroadcast.get();
+//        cv.block();
 //
 //        verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
 //        verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 6de887b..d8569bc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -201,7 +201,7 @@
             Slog.w(TAG, "finish does not match active session");
             return;
         }
-        mActiveSession.cancel();
+        mActiveSession.cancelLocked();
         mActiveSession = null;
     }
 
@@ -251,7 +251,7 @@
         // If there is an active session, cancel it to allow it to clean up its window and other
         // state.
         if (mActiveSession != null) {
-            mActiveSession.cancel();
+            mActiveSession.cancelLocked();
             mActiveSession = null;
         }
         try {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index b4629f2..0b430ca0 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -19,9 +19,9 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
-import android.app.AssistContent;
-import android.app.AssistStructure;
 import android.app.IActivityManager;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
 import android.content.ClipData;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -376,6 +376,40 @@
         return false;
     }
 
+    public void cancelLocked() {
+        hideLocked();
+        mCanceled = true;
+        if (mBound) {
+            if (mSession != null) {
+                try {
+                    mSession.destroy();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Voice interation session already dead");
+                }
+            }
+            if (mSession != null) {
+                try {
+                    mAm.finishVoiceTask(mSession);
+                } catch (RemoteException e) {
+                }
+            }
+            mContext.unbindService(this);
+            try {
+                mIWindowManager.removeWindowToken(mToken);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed removing window token", e);
+            }
+            mBound = false;
+            mService = null;
+            mSession = null;
+            mInteractor = null;
+        }
+        if (mFullyBound) {
+            mContext.unbindService(mFullConnection);
+            mFullyBound = false;
+        }
+    }
+
     public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
             IVoiceInteractor interactor) {
         mSession = session;
@@ -432,39 +466,6 @@
         mService = null;
     }
 
-    public void cancel() {
-        mCanceled = true;
-        if (mBound) {
-            if (mSession != null) {
-                try {
-                    mSession.destroy();
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Voice interation session already dead");
-                }
-            }
-            if (mSession != null) {
-                try {
-                    mAm.finishVoiceTask(mSession);
-                } catch (RemoteException e) {
-                }
-            }
-            mContext.unbindService(this);
-            try {
-                mIWindowManager.removeWindowToken(mToken);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed removing window token", e);
-            }
-            mBound = false;
-            mService = null;
-            mSession = null;
-            mInteractor = null;
-        }
-        if (mFullyBound) {
-            mContext.unbindService(mFullConnection);
-            mFullyBound = false;
-        }
-    }
-
     private boolean isStructureEnabled() {
         return Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) != 0;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1d751d5..353bb5f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -51,6 +51,10 @@
     public static final String
             ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
 
+    // Below are the keys used in carrier config bundles. To add a new variable, define the key and
+    // give it a default value in sDefaults. If you need to ship a per-network override in the
+    // system image, that can be added in packages/apps/CarrierConfig.
+
     /**
      * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
      * events from the Sim.
@@ -225,6 +229,14 @@
      */
     public static final String STRING_VVM_TYPE = "string_vvm_type";
 
+    /**
+     * The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
+     * and carrier visual voicemail are not active at the same time.
+     *
+     * @hide
+     */
+    public static final String STRING_CARRIER_VVM_PACKAGE_NAME = "string_carrier_vvm_package_name";
+
     // These variables are used by the MMS service and exposed through another API, {@link
     // SmsManager}. The variable names and string values are copied from there.
     public static final String BOOL_MMS_ALIAS_ENABLED = "aliasEnabled";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f51cb65..2445bf6e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2298,7 +2298,8 @@
     }
 
     /**
-     * Returns the voice mail count. Return 0 if unavailable.
+     * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages
+     * but the count is unknown.
      * <p>
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index f66a9ce..dae1ac3 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -17,9 +17,9 @@
 package com.android.test.voiceinteraction;
 
 import android.app.ActivityManager;
+import android.app.VoiceInteractor;
 import android.app.AssistContent;
 import android.app.AssistStructure;
-import android.app.VoiceInteractor;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -71,7 +71,7 @@
     public void onCreate(Bundle args, int startFlags) {
         super.onCreate(args, startFlags);
         ActivityManager am = getContext().getSystemService(ActivityManager.class);
-        am.setWatchHeapLimit(40*1024*1024);
+        am.setWatchHeapLimit(40 * 1024 * 1024);
     }
 
     @Override
@@ -118,6 +118,30 @@
         return mContentView;
     }
 
+    public void onHandleAssist(Bundle assistBundle) {
+        if (assistBundle != null) {
+            Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+            if (assistContext != null) {
+                mAssistStructure = AssistStructure.getAssistStructure(assistContext);
+                if (mAssistStructure != null) {
+                    if (mAssistVisualizer != null) {
+                        mAssistVisualizer.setAssistStructure(mAssistStructure);
+                    }
+                }
+                AssistContent content = AssistContent.getAssistContent(assistContext);
+                if (content != null) {
+                    Log.i(TAG, "Assist intent: " + content.getIntent());
+                    Log.i(TAG, "Assist clipdata: " + content.getClipData());
+                }
+                return;
+            }
+        }
+        if (mAssistVisualizer != null) {
+            mAssistVisualizer.clearAssistData();
+        }
+    }
+
+    /*
     @Override
     public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
         mAssistStructure = structure;
@@ -131,6 +155,7 @@
             Log.i(TAG, "Assist clipdata: " + content.getClipData());
         }
     }
+    */
 
     @Override
     public void onHandleScreenshot(Bitmap screenshot) {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 23d679d..8c128d3 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -27,7 +27,6 @@
 sources := \
 	BigBuffer.cpp \
 	BinaryResourceParser.cpp \
-	BinaryXmlPullParser.cpp \
 	BindingXmlPullParser.cpp \
 	ConfigDescription.cpp \
 	Debug.cpp \
@@ -53,6 +52,7 @@
 	ScopedXmlPullParser.cpp \
 	SourceXmlPullParser.cpp \
 	XliffXmlPullParser.cpp \
+	XmlDom.cpp \
 	XmlFlattener.cpp \
 	ZipEntry.cpp \
 	ZipFile.cpp
@@ -76,6 +76,7 @@
 	StringPool_test.cpp \
 	Util_test.cpp \
 	XliffXmlPullParser_test.cpp \
+	XmlDom_test.cpp \
 	XmlFlattener_test.cpp
 
 cIncludes := \
diff --git a/tools/aapt2/BinaryXmlPullParser.cpp b/tools/aapt2/BinaryXmlPullParser.cpp
deleted file mode 100644
index 476a215..0000000
--- a/tools/aapt2/BinaryXmlPullParser.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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.
- */
-
-#include "BinaryXmlPullParser.h"
-#include "Maybe.h"
-#include "Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-static XmlPullParser::Event codeToEvent(android::ResXMLParser::event_code_t code) {
-    switch (code) {
-        case android::ResXMLParser::START_DOCUMENT:
-            return XmlPullParser::Event::kStartDocument;
-        case android::ResXMLParser::END_DOCUMENT:
-            return XmlPullParser::Event::kEndDocument;
-        case android::ResXMLParser::START_NAMESPACE:
-            return XmlPullParser::Event::kStartNamespace;
-        case android::ResXMLParser::END_NAMESPACE:
-            return XmlPullParser::Event::kEndNamespace;
-        case android::ResXMLParser::START_TAG:
-            return XmlPullParser::Event::kStartElement;
-        case android::ResXMLParser::END_TAG:
-            return XmlPullParser::Event::kEndElement;
-        case android::ResXMLParser::TEXT:
-            return XmlPullParser::Event::kText;
-        default:
-            break;
-    }
-    return XmlPullParser::Event::kBadDocument;
-}
-
-BinaryXmlPullParser::BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser)
-    : mParser(parser), mEvent(Event::kStartDocument), mHasComment(false), sEmpty(), sEmpty8(),
-      mDepth(0) {
-}
-
-XmlPullParser::Event BinaryXmlPullParser::next() {
-    mStr1.clear();
-    mStr2.clear();
-    mAttributes.clear();
-
-    android::ResXMLParser::event_code_t code;
-    if (mHasComment) {
-        mHasComment = false;
-        code = mParser->getEventType();
-    } else {
-        code = mParser->next();
-        if (code != android::ResXMLParser::BAD_DOCUMENT) {
-            size_t len;
-            const char16_t* comment = mParser->getComment(&len);
-            if (comment) {
-                mHasComment = true;
-                mStr1.assign(comment, len);
-                return XmlPullParser::Event::kComment;
-            }
-        }
-    }
-
-    size_t len;
-    const char16_t* data;
-    mEvent = codeToEvent(code);
-    switch (mEvent) {
-        case Event::kStartNamespace:
-        case Event::kEndNamespace: {
-            data = mParser->getNamespacePrefix(&len);
-            if (data) {
-                mStr1.assign(data, len);
-            } else {
-                mStr1.clear();
-            }
-            data = mParser->getNamespaceUri(&len);
-            if (data) {
-                mStr2.assign(data, len);
-            } else {
-                mStr2.clear();
-            }
-
-            Maybe<std::u16string> result = util::extractPackageFromNamespace(mStr2);
-            if (result) {
-                if (mEvent == Event::kStartNamespace) {
-                    mPackageAliases.emplace_back(mStr1, result.value());
-                } else {
-                    assert(mPackageAliases.back().second == result.value());
-                    mPackageAliases.pop_back();
-                }
-            }
-            break;
-        }
-
-        case Event::kStartElement:
-            copyAttributes();
-            // fallthrough
-
-        case Event::kEndElement:
-            data = mParser->getElementNamespace(&len);
-            if (data) {
-                mStr1.assign(data, len);
-            } else {
-                mStr1.clear();
-            }
-            data = mParser->getElementName(&len);
-            if (data) {
-                mStr2.assign(data, len);
-            } else {
-                mStr2.clear();
-            }
-            break;
-
-        case Event::kText:
-            data = mParser->getText(&len);
-            if (data) {
-                mStr1.assign(data, len);
-            } else {
-                mStr1.clear();
-            }
-            break;
-
-        default:
-            break;
-    }
-    return mEvent;
-}
-
-XmlPullParser::Event BinaryXmlPullParser::getEvent() const {
-    if (mHasComment) {
-        return XmlPullParser::Event::kComment;
-    }
-    return mEvent;
-}
-
-const std::string& BinaryXmlPullParser::getLastError() const {
-    return sEmpty8;
-}
-
-const std::u16string& BinaryXmlPullParser::getComment() const {
-    if (mHasComment) {
-        return mStr1;
-    }
-    return sEmpty;
-}
-
-size_t BinaryXmlPullParser::getLineNumber() const {
-    return mParser->getLineNumber();
-}
-
-size_t BinaryXmlPullParser::getDepth() const {
-    return mDepth;
-}
-
-const std::u16string& BinaryXmlPullParser::getText() const {
-    if (!mHasComment && mEvent == XmlPullParser::Event::kText) {
-        return mStr1;
-    }
-    return sEmpty;
-}
-
-const std::u16string& BinaryXmlPullParser::getNamespacePrefix() const {
-    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
-            mEvent == XmlPullParser::Event::kEndNamespace)) {
-        return mStr1;
-    }
-    return sEmpty;
-}
-
-const std::u16string& BinaryXmlPullParser::getNamespaceUri() const {
-    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
-            mEvent == XmlPullParser::Event::kEndNamespace)) {
-        return mStr2;
-    }
-    return sEmpty;
-}
-
-bool BinaryXmlPullParser::applyPackageAlias(std::u16string* package,
-                                            const std::u16string& defaultPackage) const {
-    const auto endIter = mPackageAliases.rend();
-    for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
-        if (iter->first == *package) {
-            if (iter->second.empty()) {
-                *package = defaultPackage;
-            } else {
-                *package = iter->second;
-            }
-            return true;
-        }
-    }
-    return false;
-}
-
-const std::u16string& BinaryXmlPullParser::getElementNamespace() const {
-    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
-            mEvent == XmlPullParser::Event::kEndElement)) {
-        return mStr1;
-    }
-    return sEmpty;
-}
-
-const std::u16string& BinaryXmlPullParser::getElementName() const {
-    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
-            mEvent == XmlPullParser::Event::kEndElement)) {
-        return mStr2;
-    }
-    return sEmpty;
-}
-
-size_t BinaryXmlPullParser::getAttributeCount() const {
-    return mAttributes.size();
-}
-
-XmlPullParser::const_iterator BinaryXmlPullParser::beginAttributes() const {
-    return mAttributes.begin();
-}
-
-XmlPullParser::const_iterator BinaryXmlPullParser::endAttributes() const {
-    return mAttributes.end();
-}
-
-void BinaryXmlPullParser::copyAttributes() {
-    const size_t attrCount = mParser->getAttributeCount();
-    if (attrCount > 0) {
-        mAttributes.reserve(attrCount);
-        for (size_t i = 0; i < attrCount; i++) {
-            XmlPullParser::Attribute attr;
-            size_t len;
-            const char16_t* str = mParser->getAttributeNamespace(i, &len);
-            if (str) {
-                attr.namespaceUri.assign(str, len);
-            }
-            str = mParser->getAttributeName(i, &len);
-            if (str) {
-                attr.name.assign(str, len);
-            }
-            str = mParser->getAttributeStringValue(i, &len);
-            if (str) {
-                attr.value.assign(str, len);
-            }
-            mAttributes.push_back(std::move(attr));
-        }
-    }
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/BinaryXmlPullParser.h b/tools/aapt2/BinaryXmlPullParser.h
deleted file mode 100644
index 16fc8b7..0000000
--- a/tools/aapt2/BinaryXmlPullParser.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef AAPT_BINARY_XML_PULL_PARSER_H
-#define AAPT_BINARY_XML_PULL_PARSER_H
-
-#include "XmlPullParser.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace aapt {
-
-/**
- * Wraps a ResTable into the canonical XmlPullParser interface.
- */
-class BinaryXmlPullParser : public XmlPullParser {
-public:
-    BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser);
-    BinaryXmlPullParser(const BinaryXmlPullParser& rhs) = delete;
-
-    Event getEvent() const override;
-    const std::string& getLastError() const override;
-    Event next() override;
-
-    const std::u16string& getComment() const override;
-    size_t getLineNumber() const override;
-    size_t getDepth() const override;
-
-    const std::u16string& getText() const override;
-
-    const std::u16string& getNamespacePrefix() const override;
-    const std::u16string& getNamespaceUri() const override;
-    bool applyPackageAlias(std::u16string* package, const std::u16string& defaultpackage)
-            const override;
-
-    const std::u16string& getElementNamespace() const override;
-    const std::u16string& getElementName() const override;
-
-    const_iterator beginAttributes() const override;
-    const_iterator endAttributes() const override;
-    size_t getAttributeCount() const override;
-
-private:
-    void copyAttributes();
-
-    std::shared_ptr<android::ResXMLTree> mParser;
-    std::u16string mStr1;
-    std::u16string mStr2;
-    std::vector<Attribute> mAttributes;
-    Event mEvent;
-    bool mHasComment;
-    const std::u16string sEmpty;
-    const std::string sEmpty8;
-    size_t mDepth;
-    std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
-};
-
-} // namespace aapt
-
-#endif // AAPT_BINARY_XML_PULL_PARSER_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 91639c5..84957b4 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -17,7 +17,6 @@
 #include "AppInfo.h"
 #include "BigBuffer.h"
 #include "BinaryResourceParser.h"
-#include "BinaryXmlPullParser.h"
 #include "BindingXmlPullParser.h"
 #include "Debug.h"
 #include "Files.h"
@@ -128,7 +127,7 @@
                     auto iter = style.entries.begin();
                     while (iter != style.entries.end()) {
                         if (iter->key.name.package == u"android") {
-                            size_t sdkLevel = findAttributeSdkLevel(iter->key.name.entry);
+                            size_t sdkLevel = findAttributeSdkLevel(iter->key.name);
                             if (sdkLevel > 1 && sdkLevel > configValue.config.sdkVersion) {
                                 // Record that we are about to strip this.
                                 stripped.emplace_back(std::move(*iter));
@@ -300,6 +299,42 @@
     ResourceName dumpStyleTarget;
 };
 
+struct IdCollector : public xml::Visitor {
+    IdCollector(const Source& source, const std::shared_ptr<ResourceTable>& table) :
+            mSource(source), mTable(table) {
+    }
+
+    virtual void visit(xml::Text* node) override {}
+
+    virtual void visit(xml::Namespace* node) override {
+        for (const auto& child : node->children) {
+            child->accept(this);
+        }
+    }
+
+    virtual void visit(xml::Element* node) override {
+        for (const xml::Attribute& attr : node->attributes) {
+            bool create = false;
+            bool priv = false;
+            ResourceNameRef nameRef;
+            if (ResourceParser::tryParseReference(attr.value, &nameRef, &create, &priv)) {
+                if (create) {
+                    mTable->addResource(nameRef, {}, mSource.line(node->lineNumber),
+                                        util::make_unique<Id>());
+                }
+            }
+        }
+
+        for (const auto& child : node->children) {
+            child->accept(this);
+        }
+    }
+
+private:
+    Source mSource;
+    std::shared_ptr<ResourceTable> mTable;
+};
+
 bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
                 const CompileItem& item, ZipFile* outApk) {
     std::ifstream in(item.source.path, std::ifstream::binary);
@@ -308,20 +343,19 @@
         return false;
     }
 
+    SourceLogger logger(item.source);
+    std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
+    if (!root) {
+        return false;
+    }
+
+    // Collect any resource ID's declared here.
+    IdCollector idCollector(item.source, table);
+    root->accept(&idCollector);
+
     BigBuffer outBuffer(1024);
-
-    // No resolver, since we are not compiling attributes here.
-    XmlFlattener flattener(table, {});
-
-    XmlFlattener::Options xmlOptions;
-    xmlOptions.defaultPackage = table->getPackage();
-    xmlOptions.keepRawValues = true;
-
-    std::shared_ptr<XmlPullParser> parser = std::make_shared<SourceXmlPullParser>(in);
-
-    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
-                                                     xmlOptions);
-    if (!minStrippedSdk) {
+    if (!xml::flatten(root.get(), options.appInfo.package, &outBuffer)) {
+        logger.error() << "failed to encode XML." << std::endl;
         return false;
     }
 
@@ -369,19 +403,13 @@
 bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
              const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
              const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) {
-    std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
-    if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
+    SourceLogger logger(item.source);
+    std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
+    if (!root) {
         return false;
     }
 
-    std::shared_ptr<XmlPullParser> parser = std::make_shared<BinaryXmlPullParser>(tree);
-
-    BigBuffer outBuffer(1024);
-    XmlFlattener flattener({}, resolver);
-
-    XmlFlattener::Options xmlOptions;
-    xmlOptions.defaultPackage = item.originalPackage;
-
+    xml::FlattenOptions xmlOptions;
     if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
         xmlOptions.keepRawValues = true;
     }
@@ -392,16 +420,12 @@
         xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
     }
 
-    std::shared_ptr<BindingXmlPullParser> binding;
-    if (item.name.type == ResourceType::kLayout) {
-        // Layouts may have defined bindings, so we need to make sure they get processed.
-        binding = std::make_shared<BindingXmlPullParser>(parser);
-        parser = binding;
-    }
-
-    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
-                                                     xmlOptions);
+    BigBuffer outBuffer(1024);
+    Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
+                                                       item.originalPackage, resolver,
+                                                       xmlOptions, &outBuffer);
     if (!minStrippedSdk) {
+        logger.error() << "failed to encode XML." << std::endl;
         return false;
     }
 
@@ -431,30 +455,6 @@
                                       << buildFileReference(item) << "' to apk." << std::endl;
         return false;
     }
-
-    if (binding && !options.bindingOutput.path.empty()) {
-        // We generated a binding xml file, write it out.
-        Source bindingOutput = options.bindingOutput;
-        appendPath(&bindingOutput.path, buildFileReference(item));
-
-        if (!mkdirs(bindingOutput.path)) {
-            Logger::error(bindingOutput) << strerror(errno) << std::endl;
-            return false;
-        }
-
-        appendPath(&bindingOutput.path, "bind.xml");
-
-        std::ofstream bout(bindingOutput.path);
-        if (!bout) {
-            Logger::error(bindingOutput) << strerror(errno) << std::endl;
-            return false;
-        }
-
-        if (!binding->writeToFile(bout)) {
-            Logger::error(bindingOutput) << strerror(errno) << std::endl;
-            return false;
-        }
-    }
     return true;
 }
 
@@ -504,13 +504,15 @@
         return false;
     }
 
-    BigBuffer outBuffer(1024);
-    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
-    XmlFlattener flattener({}, resolver);
+    SourceLogger logger(options.manifest);
+    std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
+    if (!root) {
+        return false;
+    }
 
-    XmlFlattener::Options xmlOptions;
-    xmlOptions.defaultPackage = options.appInfo.package;
-    if (!flattener.flatten(options.manifest, xmlParser, &outBuffer, xmlOptions)) {
+    BigBuffer outBuffer(1024);
+    if (!xml::flattenAndLink(options.manifest, root.get(), options.appInfo.package, resolver, {},
+                &outBuffer)) {
         return false;
     }
 
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 3f156a6..9bdae49 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -14,11 +14,51 @@
  * limitations under the License.
  */
 
+#include "SdkConstants.h"
+
+#include <algorithm>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 namespace aapt {
 
+static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
+    { 0x021c, 1 },
+    { 0x021d, 2 },
+    { 0x0269, SDK_CUPCAKE },
+    { 0x028d, SDK_DONUT },
+    { 0x02ad, SDK_ECLAIR },
+    { 0x02b3, SDK_ECLAIR_0_1 },
+    { 0x02b5, SDK_ECLAIR_MR1 },
+    { 0x02bd, SDK_FROYO },
+    { 0x02cb, SDK_GINGERBREAD },
+    { 0x0361, SDK_HONEYCOMB },
+    { 0x0366, SDK_HONEYCOMB_MR1 },
+    { 0x03a6, SDK_HONEYCOMB_MR2 },
+    { 0x03ae, SDK_JELLY_BEAN },
+    { 0x03cc, SDK_JELLY_BEAN_MR1 },
+    { 0x03da, SDK_JELLY_BEAN_MR2 },
+    { 0x03f1, SDK_KITKAT },
+    { 0x03f6, SDK_KITKAT_WATCH },
+    { 0x04ce, SDK_LOLLIPOP },
+};
+
+static bool lessEntryId(const std::pair<uint16_t, size_t>& p, uint16_t entryId) {
+    return p.first < entryId;
+}
+
+size_t findAttributeSdkLevel(ResourceId id) {
+    if (id.packageId() != 0x01 && id.typeId() != 0x01) {
+        return 0;
+    }
+    auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entryId(), lessEntryId);
+    if (iter == sAttrIdMap.end()) {
+        return SDK_LOLLIPOP_MR1;
+    }
+    return iter->second;
+}
+
 static const std::unordered_map<std::u16string, size_t> sAttrMap = {
     { u"marqueeRepeatLimit", 2 },
     { u"windowNoDisplay", 3 },
@@ -682,12 +722,16 @@
     { u"colorEdgeEffect", 21 }
 };
 
-size_t findAttributeSdkLevel(const std::u16string& name) {
-    auto iter = sAttrMap.find(name);
+size_t findAttributeSdkLevel(const ResourceName& name) {
+    if (name.package != u"android" && name.type != ResourceType::kAttr) {
+        return 0;
+    }
+
+    auto iter = sAttrMap.find(name.entry);
     if (iter != sAttrMap.end()) {
         return iter->second;
     }
-    return 0;
+    return SDK_LOLLIPOP_MR1;
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 469c355..803da03 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -19,8 +19,6 @@
 
 #include "Resource.h"
 
-#include <string>
-
 namespace aapt {
 
 enum {
@@ -46,7 +44,8 @@
     SDK_LOLLIPOP_MR1 = 22,
 };
 
-size_t findAttributeSdkLevel(const std::u16string& name);
+size_t findAttributeSdkLevel(ResourceId id);
+size_t findAttributeSdkLevel(const ResourceName& name);
 
 } // namespace aapt
 
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/XmlDom.cpp
new file mode 100644
index 0000000..763029f
--- /dev/null
+++ b/tools/aapt2/XmlDom.cpp
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+#include "Logger.h"
+#include "Util.h"
+#include "XmlDom.h"
+#include "XmlPullParser.h"
+
+#include <cassert>
+#include <memory>
+#include <stack>
+#include <string>
+#include <tuple>
+
+namespace aapt {
+namespace xml {
+
+constexpr char kXmlNamespaceSep = 1;
+
+struct Stack {
+    std::unique_ptr<xml::Node> root;
+    std::stack<xml::Node*> nodeStack;
+    std::u16string pendingComment;
+};
+
+/**
+ * Extracts the namespace and name of an expanded element or attribute name.
+ */
+static void splitName(const char* name, std::u16string* outNs, std::u16string* outName) {
+    const char* p = name;
+    while (*p != 0 && *p != kXmlNamespaceSep) {
+        p++;
+    }
+
+    if (*p == 0) {
+        outNs->clear();
+        *outName = util::utf8ToUtf16(name);
+    } else {
+        *outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
+        *outName = util::utf8ToUtf16(p + 1);
+    }
+}
+
+static void addToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) {
+    node->lineNumber = XML_GetCurrentLineNumber(parser);
+    node->columnNumber = XML_GetCurrentColumnNumber(parser);
+
+    Node* thisNode = node.get();
+    if (!stack->nodeStack.empty()) {
+        stack->nodeStack.top()->addChild(std::move(node));
+    } else {
+        stack->root = std::move(node);
+    }
+
+    if (thisNode->type != NodeType::kText) {
+        stack->nodeStack.push(thisNode);
+    }
+}
+
+static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
+    if (prefix) {
+        ns->namespacePrefix = util::utf8ToUtf16(prefix);
+    }
+
+    if (uri) {
+        ns->namespaceUri = util::utf8ToUtf16(uri);
+    }
+
+    addToStack(stack, parser, std::move(ns));
+}
+
+static void XMLCALL endNamespaceHandler(void* userData, const char* prefix) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    assert(!stack->nodeStack.empty());
+    stack->nodeStack.pop();
+}
+
+static bool lessAttribute(const Attribute& lhs, const Attribute& rhs) {
+    return std::tie(lhs.namespaceUri, lhs.name, lhs.value) <
+            std::tie(rhs.namespaceUri, rhs.name, rhs.value);
+}
+
+static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    std::unique_ptr<Element> el = util::make_unique<Element>();
+    splitName(name, &el->namespaceUri, &el->name);
+
+    while (*attrs) {
+        Attribute attribute;
+        splitName(*attrs++, &attribute.namespaceUri, &attribute.name);
+        attribute.value = util::utf8ToUtf16(*attrs++);
+
+        // Insert in sorted order.
+        auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
+                                     lessAttribute);
+        el->attributes.insert(iter, std::move(attribute));
+    }
+
+    el->comment = std::move(stack->pendingComment);
+    addToStack(stack, parser, std::move(el));
+}
+
+static void XMLCALL endElementHandler(void* userData, const char* name) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    assert(!stack->nodeStack.empty());
+    stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+    stack->nodeStack.pop();
+}
+
+static void XMLCALL characterDataHandler(void* userData, const char* s, int len) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    if (!s || len <= 0) {
+        return;
+    }
+
+    // See if we can just append the text to a previous text node.
+    if (!stack->nodeStack.empty()) {
+        Node* currentParent = stack->nodeStack.top();
+        if (!currentParent->children.empty()) {
+            Node* lastChild = currentParent->children.back().get();
+            if (lastChild->type == NodeType::kText) {
+                Text* text = static_cast<Text*>(lastChild);
+                text->text += util::utf8ToUtf16(StringPiece(s, len));
+                return;
+            }
+        }
+    }
+
+    std::unique_ptr<Text> text = util::make_unique<Text>();
+    text->text = util::utf8ToUtf16(StringPiece(s, len));
+    addToStack(stack, parser, std::move(text));
+}
+
+static void XMLCALL commentDataHandler(void* userData, const char* comment) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    if (!stack->pendingComment.empty()) {
+        stack->pendingComment += '\n';
+    }
+    stack->pendingComment += util::utf8ToUtf16(comment);
+}
+
+std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger) {
+    Stack stack;
+
+    XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+    XML_SetUserData(parser, &stack);
+    XML_UseParserAsHandlerArg(parser);
+    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
+    XML_SetNamespaceDeclHandler(parser, startNamespaceHandler, endNamespaceHandler);
+    XML_SetCharacterDataHandler(parser, characterDataHandler);
+    XML_SetCommentHandler(parser, commentDataHandler);
+
+    char buffer[1024];
+    while (!in->eof()) {
+        in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
+        if (in->bad() && !in->eof()) {
+            stack.root = {};
+            logger->error() << strerror(errno) << std::endl;
+            break;
+        }
+
+        if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
+            stack.root = {};
+            logger->error(XML_GetCurrentLineNumber(parser))
+                    << XML_ErrorString(XML_GetErrorCode(parser)) << std::endl;
+            break;
+        }
+    }
+
+    XML_ParserFree(parser);
+    return std::move(stack.root);
+}
+
+static void copyAttributes(Element* el, android::ResXMLParser* parser) {
+    const size_t attrCount = parser->getAttributeCount();
+    if (attrCount > 0) {
+        el->attributes.reserve(attrCount);
+        for (size_t i = 0; i < attrCount; i++) {
+            Attribute attr;
+            size_t len;
+            const char16_t* str16 = parser->getAttributeNamespace(i, &len);
+            if (str16) {
+                attr.namespaceUri.assign(str16, len);
+            }
+
+            str16 = parser->getAttributeName(i, &len);
+            if (str16) {
+                attr.name.assign(str16, len);
+            }
+
+            str16 = parser->getAttributeStringValue(i, &len);
+            if (str16) {
+                attr.value.assign(str16, len);
+            }
+            el->attributes.push_back(std::move(attr));
+        }
+    }
+}
+
+std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger) {
+    std::unique_ptr<Node> root;
+    std::stack<Node*> nodeStack;
+
+    android::ResXMLTree tree;
+    if (tree.setTo(data, dataLen) != android::NO_ERROR) {
+        return {};
+    }
+
+    android::ResXMLParser::event_code_t code;
+    while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
+            code != android::ResXMLParser::END_DOCUMENT) {
+        std::unique_ptr<Node> newNode;
+        switch (code) {
+            case android::ResXMLParser::START_NAMESPACE: {
+                std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
+                size_t len;
+                const char16_t* str16 = tree.getNamespacePrefix(&len);
+                if (str16) {
+                    node->namespacePrefix.assign(str16, len);
+                }
+
+                str16 = tree.getNamespaceUri(&len);
+                if (str16) {
+                    node->namespaceUri.assign(str16, len);
+                }
+                newNode = std::move(node);
+                break;
+            }
+
+            case android::ResXMLParser::START_TAG: {
+                std::unique_ptr<Element> node = util::make_unique<Element>();
+                size_t len;
+                const char16_t* str16 = tree.getElementNamespace(&len);
+                if (str16) {
+                    node->namespaceUri.assign(str16, len);
+                }
+
+                str16 = tree.getElementName(&len);
+                if (str16) {
+                    node->name.assign(str16, len);
+                }
+
+                copyAttributes(node.get(), &tree);
+
+                newNode = std::move(node);
+                break;
+            }
+
+            case android::ResXMLParser::TEXT: {
+                std::unique_ptr<Text> node = util::make_unique<Text>();
+                size_t len;
+                const char16_t* str16 = tree.getText(&len);
+                if (str16) {
+                    node->text.assign(str16, len);
+                }
+                newNode = std::move(node);
+                break;
+            }
+
+            case android::ResXMLParser::END_NAMESPACE:
+            case android::ResXMLParser::END_TAG:
+                assert(!nodeStack.empty());
+                nodeStack.pop();
+                break;
+
+            default:
+                assert(false);
+                break;
+        }
+
+        if (newNode) {
+            newNode->lineNumber = tree.getLineNumber();
+
+            Node* thisNode = newNode.get();
+            if (!root) {
+                assert(nodeStack.empty());
+                root = std::move(newNode);
+            } else {
+                assert(!nodeStack.empty());
+                nodeStack.top()->addChild(std::move(newNode));
+            }
+
+            if (thisNode->type != NodeType::kText) {
+                nodeStack.push(thisNode);
+            }
+        }
+    }
+    return std::move(root);
+}
+
+Node::Node(NodeType type) : type(type), parent(nullptr), lineNumber(0), columnNumber(0) {
+}
+
+void Node::addChild(std::unique_ptr<Node> child) {
+    child->parent = this;
+    children.push_back(std::move(child));
+}
+
+Namespace::Namespace() : BaseNode(NodeType::kNamespace) {
+}
+
+std::unique_ptr<Node> Namespace::clone() const {
+    Namespace* ns = new Namespace();
+    ns->lineNumber = lineNumber;
+    ns->columnNumber = columnNumber;
+    ns->comment = comment;
+    ns->namespacePrefix = namespacePrefix;
+    ns->namespaceUri = namespaceUri;
+    for (auto& child : children) {
+        ns->addChild(child->clone());
+    }
+    return std::unique_ptr<Node>(ns);
+}
+
+Element::Element() : BaseNode(NodeType::kElement) {
+}
+
+std::unique_ptr<Node> Element::clone() const {
+    Element* el = new Element();
+    el->lineNumber = lineNumber;
+    el->columnNumber = columnNumber;
+    el->comment = comment;
+    el->namespaceUri = namespaceUri;
+    el->name = name;
+    el->attributes = attributes;
+    for (auto& child : children) {
+        el->addChild(child->clone());
+    }
+    return std::unique_ptr<Node>(el);
+}
+
+Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) {
+    for (auto& attr : attributes) {
+        if (ns == attr.namespaceUri && name == attr.name) {
+            return &attr;
+        }
+    }
+    return nullptr;
+}
+
+Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) {
+    return findChildWithAttribute(ns, name, nullptr);
+}
+
+Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
+                                         const Attribute* reqAttr) {
+    for (auto& childNode : children) {
+        Node* child = childNode.get();
+        while (child->type == NodeType::kNamespace) {
+            if (child->children.empty()) {
+                break;
+            }
+            child = child->children[0].get();
+        }
+
+        if (child->type == NodeType::kElement) {
+            Element* el = static_cast<Element*>(child);
+            if (ns == el->namespaceUri && name == el->name) {
+                if (!reqAttr) {
+                    return el;
+                }
+
+                Attribute* attrName = el->findAttribute(reqAttr->namespaceUri, reqAttr->name);
+                if (attrName && attrName->value == reqAttr->value) {
+                    return el;
+                }
+            }
+        }
+    }
+    return nullptr;
+}
+
+std::vector<Element*> Element::getChildElements() {
+    std::vector<Element*> elements;
+    for (auto& childNode : children) {
+        Node* child = childNode.get();
+        while (child->type == NodeType::kNamespace) {
+            if (child->children.empty()) {
+                break;
+            }
+            child = child->children[0].get();
+        }
+
+        if (child->type == NodeType::kElement) {
+            elements.push_back(static_cast<Element*>(child));
+        }
+    }
+    return elements;
+}
+
+Text::Text() : BaseNode(NodeType::kText) {
+}
+
+std::unique_ptr<Node> Text::clone() const {
+    Text* el = new Text();
+    el->lineNumber = lineNumber;
+    el->columnNumber = columnNumber;
+    el->comment = comment;
+    el->text = text;
+    return std::unique_ptr<Node>(el);
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h
new file mode 100644
index 0000000..6931884
--- /dev/null
+++ b/tools/aapt2/XmlDom.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_XML_DOM_H
+#define AAPT_XML_DOM_H
+
+#include "Logger.h"
+#include "StringPiece.h"
+
+#include <istream>
+#include <libexpat/expat.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace xml {
+
+struct Visitor;
+
+/**
+ * The type of node. Can be used to downcast to the concrete XML node
+ * class.
+ */
+enum class NodeType {
+    kNamespace,
+    kElement,
+    kText,
+};
+
+/**
+ * Base class for all XML nodes.
+ */
+struct Node {
+    NodeType type;
+    Node* parent;
+    size_t lineNumber;
+    size_t columnNumber;
+    std::u16string comment;
+    std::vector<std::unique_ptr<Node>> children;
+
+    Node(NodeType type);
+    void addChild(std::unique_ptr<Node> child);
+    virtual std::unique_ptr<Node> clone() const = 0;
+    virtual void accept(Visitor* visitor) = 0;
+    virtual ~Node() {}
+};
+
+/**
+ * Base class that implements the visitor methods for a
+ * subclass of Node.
+ */
+template <typename Derived>
+struct BaseNode : public Node {
+    BaseNode(NodeType t);
+    virtual void accept(Visitor* visitor) override;
+};
+
+/**
+ * A Namespace XML node. Can only have one child.
+ */
+struct Namespace : public BaseNode<Namespace> {
+    std::u16string namespacePrefix;
+    std::u16string namespaceUri;
+
+    Namespace();
+    virtual std::unique_ptr<Node> clone() const override;
+};
+
+/**
+ * An XML attribute.
+ */
+struct Attribute {
+    std::u16string namespaceUri;
+    std::u16string name;
+    std::u16string value;
+};
+
+/**
+ * An Element XML node.
+ */
+struct Element : public BaseNode<Element> {
+    std::u16string namespaceUri;
+    std::u16string name;
+    std::vector<Attribute> attributes;
+
+    Element();
+    virtual std::unique_ptr<Node> clone() const override;
+    Attribute* findAttribute(const StringPiece16& ns, const StringPiece16& name);
+    xml::Element* findChild(const StringPiece16& ns, const StringPiece16& name);
+    xml::Element* findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
+                                         const xml::Attribute* reqAttr);
+    std::vector<xml::Element*> getChildElements();
+};
+
+/**
+ * A Text (CDATA) XML node. Can not have any children.
+ */
+struct Text : public BaseNode<Text> {
+    std::u16string text;
+
+    Text();
+    virtual std::unique_ptr<Node> clone() const override;
+};
+
+/**
+ * Inflates an XML DOM from a text stream, logging errors to the logger.
+ * Returns the root node on success, or nullptr on failure.
+ */
+std::unique_ptr<Node> inflate(std::istream* in, SourceLogger* logger);
+
+/**
+ * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
+ * Returns the root node on success, or nullptr on failure.
+ */
+std::unique_ptr<Node> inflate(const void* data, size_t dataLen, SourceLogger* logger);
+
+/**
+ * A visitor interface for the different XML Node subtypes.
+ */
+struct Visitor {
+    virtual void visit(Namespace* node) = 0;
+    virtual void visit(Element* node) = 0;
+    virtual void visit(Text* text) = 0;
+};
+
+// Implementations
+
+template <typename Derived>
+BaseNode<Derived>::BaseNode(NodeType type) : Node(type) {
+}
+
+template <typename Derived>
+void BaseNode<Derived>::accept(Visitor* visitor) {
+    visitor->visit(static_cast<Derived*>(this));
+}
+
+} // namespace xml
+} // namespace aapt
+
+#endif // AAPT_XML_DOM_H
diff --git a/tools/aapt2/XmlDom_test.cpp b/tools/aapt2/XmlDom_test.cpp
new file mode 100644
index 0000000..0217144
--- /dev/null
+++ b/tools/aapt2/XmlDom_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include "XmlDom.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+
+TEST(XmlDomTest, Inflate) {
+    std::stringstream in(kXmlPreamble);
+    in << R"EOF(
+        <Layout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <TextView android:id="@+id/id"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content" />
+        </Layout>
+    )EOF";
+
+    SourceLogger logger(Source{ "/test/path" });
+    std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
+    ASSERT_NE(root, nullptr);
+
+    EXPECT_EQ(root->type, xml::NodeType::kNamespace);
+    xml::Namespace* ns = static_cast<xml::Namespace*>(root.get());
+    EXPECT_EQ(ns->namespaceUri, u"http://schemas.android.com/apk/res/android");
+    EXPECT_EQ(ns->namespacePrefix, u"android");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.cpp b/tools/aapt2/XmlFlattener.cpp
index f78e38d..56b5613d 100644
--- a/tools/aapt2/XmlFlattener.cpp
+++ b/tools/aapt2/XmlFlattener.cpp
@@ -34,425 +34,444 @@
 #include <vector>
 
 namespace aapt {
+namespace xml {
 
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+constexpr uint32_t kLowPriority = 0xffffffffu;
 
-struct AttributeValueFlattener : ValueVisitor {
-    AttributeValueFlattener(
-            std::shared_ptr<IResolver> resolver, SourceLogger* logger,
-            android::Res_value* outValue, std::shared_ptr<XmlPullParser> parser, bool* outError,
-            StringPool::Ref rawValue, std::u16string* defaultPackage,
-            std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>* outStringRefs) :
-            mResolver(resolver), mLogger(logger), mOutValue(outValue), mParser(parser),
-            mError(outError), mRawValue(rawValue), mDefaultPackage(defaultPackage),
-            mStringRefs(outStringRefs) {
+// A vector that maps String refs to their final destination in the out buffer.
+using FlatStringRefList = std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>;
+
+struct XmlFlattener : public Visitor {
+    XmlFlattener(BigBuffer* outBuffer, StringPool* pool, FlatStringRefList* stringRefs,
+                 const std::u16string& defaultPackage) :
+            mOut(outBuffer), mPool(pool), mStringRefs(stringRefs),
+            mDefaultPackage(defaultPackage) {
     }
 
-    void visit(Reference& reference, ValueVisitorArgs&) override {
-        // First see if we can convert the package name from a prefix to a real
-        // package name.
-        ResourceName aliasedName = reference.name;
+    // No copying.
+    XmlFlattener(const XmlFlattener&) = delete;
+    XmlFlattener& operator=(const XmlFlattener&) = delete;
 
-        if (!reference.name.package.empty()) {
-            // Only if we specified a package do we look for its alias.
-            mParser->applyPackageAlias(&reference.name.package, *mDefaultPackage);
-        } else {
-            reference.name.package = *mDefaultPackage;
+    void writeNamespace(Namespace* node, uint16_t type) {
+        const size_t startIndex = mOut->size();
+        android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
+        android::ResXMLTree_namespaceExt* flatNs =
+                mOut->nextBlock<android::ResXMLTree_namespaceExt>();
+        mOut->align4();
+
+        flatNode->header = { type, sizeof(*flatNode), (uint32_t)(mOut->size() - startIndex) };
+        flatNode->lineNumber = node->lineNumber;
+        flatNode->comment.index = -1;
+        addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
+        addString(node->namespaceUri, kLowPriority, &flatNs->uri);
+    }
+
+    virtual void visit(Namespace* node) override {
+        // Extract the package/prefix from this namespace node.
+        Maybe<std::u16string> package = util::extractPackageFromNamespace(node->namespaceUri);
+        if (package) {
+            mPackageAliases.emplace_back(
+                    node->namespacePrefix,
+                    package.value().empty() ? mDefaultPackage : package.value());
         }
 
-        Maybe<ResourceId> result = mResolver->findId(reference.name);
-        if (!result || !result.value().isValid()) {
-            std::ostream& out = mLogger->error(mParser->getLineNumber())
-                    << "unresolved reference '"
-                    << aliasedName
-                    << "'";
-            if (aliasedName != reference.name) {
-                out << " (aka '" << reference.name << "')";
+        writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
+        for (const auto& child : node->children) {
+            child->accept(this);
+        }
+        writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
+
+        if (package) {
+            mPackageAliases.pop_back();
+        }
+    }
+
+    virtual void visit(Text* node) override {
+        if (util::trimWhitespace(node->text).empty()) {
+            return;
+        }
+
+        const size_t startIndex = mOut->size();
+        android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
+        android::ResXMLTree_cdataExt* flatText = mOut->nextBlock<android::ResXMLTree_cdataExt>();
+        mOut->align4();
+
+        const uint16_t type = android::RES_XML_CDATA_TYPE;
+        flatNode->header = { type, sizeof(*flatNode), (uint32_t)(mOut->size() - startIndex) };
+        flatNode->lineNumber = node->lineNumber;
+        flatNode->comment.index = -1;
+        addString(node->text, kLowPriority, &flatText->data);
+    }
+
+    virtual void visit(Element* node) override {
+        const size_t startIndex = mOut->size();
+        android::ResXMLTree_node* flatNode = mOut->nextBlock<android::ResXMLTree_node>();
+        android::ResXMLTree_attrExt* flatElem = mOut->nextBlock<android::ResXMLTree_attrExt>();
+
+        const uint16_t type = android::RES_XML_START_ELEMENT_TYPE;
+        flatNode->header = { type, sizeof(*flatNode), 0 };
+        flatNode->lineNumber = node->lineNumber;
+        flatNode->comment.index = -1;
+
+        addString(node->namespaceUri, kLowPriority, &flatElem->ns);
+        addString(node->name, kLowPriority, &flatElem->name);
+        flatElem->attributeStart = sizeof(*flatElem);
+        flatElem->attributeSize = sizeof(android::ResXMLTree_attribute);
+        flatElem->attributeCount = node->attributes.size();
+
+        if (!writeAttributes(mOut, node, flatElem)) {
+            mError = true;
+        }
+
+        mOut->align4();
+        flatNode->header.size = (uint32_t)(mOut->size() - startIndex);
+
+        for (const auto& child : node->children) {
+            child->accept(this);
+        }
+
+        const size_t startEndIndex = mOut->size();
+        android::ResXMLTree_node* flatEndNode = mOut->nextBlock<android::ResXMLTree_node>();
+        android::ResXMLTree_endElementExt* flatEndElem =
+                mOut->nextBlock<android::ResXMLTree_endElementExt>();
+        mOut->align4();
+
+        const uint16_t endType = android::RES_XML_END_ELEMENT_TYPE;
+        flatEndNode->header = { endType, sizeof(*flatEndNode),
+                (uint32_t)(mOut->size() - startEndIndex) };
+        flatEndNode->lineNumber = node->lineNumber;
+        flatEndNode->comment.index = -1;
+
+        addString(node->namespaceUri, kLowPriority, &flatEndElem->ns);
+        addString(node->name, kLowPriority, &flatEndElem->name);
+    }
+
+    bool success() const {
+        return !mError;
+    }
+
+protected:
+    void addString(const StringPiece16& str, uint32_t priority, android::ResStringPool_ref* dest) {
+        if (!str.empty()) {
+            mStringRefs->emplace_back(mPool->makeRef(str, StringPool::Context{ priority }), dest);
+        } else {
+            // The device doesn't think a string of size 0 is the same as null.
+            dest->index = -1;
+        }
+    }
+
+    void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
+        mStringRefs->emplace_back(ref, dest);
+    }
+
+    Maybe<std::u16string> getPackageAlias(const std::u16string& prefix) {
+        const auto endIter = mPackageAliases.rend();
+        for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
+            if (iter->first == prefix) {
+                return iter->second;
             }
-            out << "'." << std::endl;
-            *mError = true;
-        } else {
-            reference.id = result.value();
-            reference.flatten(*mOutValue);
         }
+        return {};
     }
 
-    void visit(String& string, ValueVisitorArgs&) override {
-        mOutValue->dataType = android::Res_value::TYPE_STRING;
-        mStringRefs->emplace_back(
-                mRawValue,
-                reinterpret_cast<android::ResStringPool_ref*>(mOutValue->data));
+    const std::u16string& getDefaultPackage() const {
+        return mDefaultPackage;
     }
 
-    void visitItem(Item& item, ValueVisitorArgs&) override {
-        item.flatten(*mOutValue);
-    }
+    /**
+     * Subclasses override this to deal with attributes. Attributes can be flattened as
+     * raw values or as resources.
+     */
+    virtual bool writeAttributes(BigBuffer* out, Element* node,
+                                 android::ResXMLTree_attrExt* flatElem) = 0;
 
 private:
-    std::shared_ptr<IResolver> mResolver;
-    SourceLogger* mLogger;
-    android::Res_value* mOutValue;
-    std::shared_ptr<XmlPullParser> mParser;
-    bool* mError;
-    StringPool::Ref mRawValue;
-    std::u16string* mDefaultPackage;
-    std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>* mStringRefs;
+    BigBuffer* mOut;
+    StringPool* mPool;
+    FlatStringRefList* mStringRefs;
+    std::u16string mDefaultPackage;
+    bool mError = false;
+    std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
 };
 
-struct XmlAttribute {
-    uint32_t resourceId;
-    const XmlPullParser::Attribute* xmlAttr;
-    const Attribute* attr;
-    StringPool::Ref nameRef;
+/**
+ * Flattens XML, encoding the attributes as raw strings. This is used in the compile phase.
+ */
+struct CompileXmlFlattener : public XmlFlattener {
+    CompileXmlFlattener(BigBuffer* outBuffer, StringPool* pool, FlatStringRefList* stringRefs,
+                        const std::u16string& defaultPackage) :
+            XmlFlattener(outBuffer, pool, stringRefs, defaultPackage) {
+    }
+
+    virtual bool writeAttributes(BigBuffer* out, Element* node,
+                                 android::ResXMLTree_attrExt* flatElem) override {
+        flatElem->attributeCount = node->attributes.size();
+        if (node->attributes.empty()) {
+            return true;
+        }
+
+        android::ResXMLTree_attribute* flatAttrs = out->nextBlock<android::ResXMLTree_attribute>(
+                node->attributes.size());
+        for (const Attribute& attr : node->attributes) {
+            addString(attr.namespaceUri, kLowPriority, &flatAttrs->ns);
+            addString(attr.name, kLowPriority, &flatAttrs->name);
+            addString(attr.value, kLowPriority, &flatAttrs->rawValue);
+            flatAttrs++;
+        }
+        return true;
+    }
 };
 
-static bool lessAttributeId(const XmlAttribute& a, uint32_t id) {
+struct AttributeToFlatten {
+    uint32_t resourceId = 0;
+    const Attribute* xmlAttr = nullptr;
+    const ::aapt::Attribute* resourceAttr = nullptr;
+};
+
+static bool lessAttributeId(const AttributeToFlatten& a, uint32_t id) {
     return a.resourceId < id;
 }
 
-XmlFlattener::XmlFlattener(const std::shared_ptr<ResourceTable>& table,
-                           const std::shared_ptr<IResolver>& resolver) :
-        mTable(table), mResolver(resolver) {
-}
+/**
+ * Flattens XML, encoding the attributes as resources.
+ */
+struct LinkedXmlFlattener : public XmlFlattener {
+    LinkedXmlFlattener(BigBuffer* outBuffer, StringPool* pool,
+                       std::map<std::u16string, StringPool>* packagePools,
+                       FlatStringRefList* stringRefs,
+                       const std::u16string& defaultPackage,
+                       const std::shared_ptr<IResolver>& resolver,
+                       SourceLogger* logger,
+                       const FlattenOptions& options) :
+            XmlFlattener(outBuffer, pool, stringRefs, defaultPackage), mResolver(resolver),
+            mLogger(logger), mPackagePools(packagePools), mOptions(options) {
+    }
+
+    virtual bool writeAttributes(BigBuffer* out, Element* node,
+                                 android::ResXMLTree_attrExt* flatElem) override {
+        bool error = false;
+        std::vector<AttributeToFlatten> sortedAttributes;
+        uint32_t nextAttributeId = 0x80000000u;
+
+        // Sort and filter attributes by their resource ID.
+        for (const Attribute& attr : node->attributes) {
+            AttributeToFlatten attrToFlatten;
+            attrToFlatten.xmlAttr = &attr;
+
+            Maybe<std::u16string> package = util::extractPackageFromNamespace(attr.namespaceUri);
+            if (package) {
+                // Find the Attribute object via our Resolver.
+                ResourceName attrName = { package.value(), ResourceType::kAttr, attr.name };
+                if (attrName.package.empty()) {
+                    attrName.package = getDefaultPackage();
+                }
+
+                Maybe<IResolver::Entry> result = mResolver->findAttribute(attrName);
+                if (!result || !result.value().id.isValid() || !result.value().attr) {
+                    error = true;
+                    mLogger->error(node->lineNumber)
+                            << "unresolved attribute '" << attrName << "'."
+                            << std::endl;
+                } else {
+                    attrToFlatten.resourceId = result.value().id.id;
+                    attrToFlatten.resourceAttr = result.value().attr;
+
+                    size_t sdk = findAttributeSdkLevel(attrToFlatten.resourceId);
+                    if (mOptions.maxSdkAttribute && sdk > mOptions.maxSdkAttribute.value()) {
+                        // We need to filter this attribute out.
+                        mSmallestFilteredSdk = std::min(mSmallestFilteredSdk, sdk);
+                        continue;
+                    }
+                }
+            }
+
+            if (attrToFlatten.resourceId == 0) {
+                // Attributes that have no resource ID (because they don't belong to a
+                // package) should appear after those that do have resource IDs. Assign
+                // them some integer value that will appear after.
+                attrToFlatten.resourceId = nextAttributeId++;
+            }
+
+            // Insert the attribute into the sorted vector.
+            auto iter = std::lower_bound(sortedAttributes.begin(), sortedAttributes.end(),
+                                         attrToFlatten.resourceId, lessAttributeId);
+            sortedAttributes.insert(iter, std::move(attrToFlatten));
+        }
+
+        flatElem->attributeCount = sortedAttributes.size();
+        if (sortedAttributes.empty()) {
+            return true;
+        }
+
+        android::ResXMLTree_attribute* flatAttr = out->nextBlock<android::ResXMLTree_attribute>(
+                sortedAttributes.size());
+
+        // Now that we have sorted the attributes into their final encoded order, it's time
+        // to actually write them out.
+        uint16_t attributeIndex = 1;
+        for (const AttributeToFlatten& attrToFlatten : sortedAttributes) {
+            Maybe<std::u16string> package = util::extractPackageFromNamespace(
+                    attrToFlatten.xmlAttr->namespaceUri);
+
+            // Assign the indices for specific attributes.
+            if (package && package.value() == u"android" && attrToFlatten.xmlAttr->name == u"id") {
+                flatElem->idIndex = attributeIndex;
+            } else if (attrToFlatten.xmlAttr->namespaceUri.empty()) {
+                if (attrToFlatten.xmlAttr->name == u"class") {
+                    flatElem->classIndex = attributeIndex;
+                } else if (attrToFlatten.xmlAttr->name == u"style") {
+                    flatElem->styleIndex = attributeIndex;
+                }
+            }
+            attributeIndex++;
+
+            // Add the namespaceUri and name to the list of StringRefs to encode.
+            addString(attrToFlatten.xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns);
+            flatAttr->rawValue.index = -1;
+
+            if (!attrToFlatten.resourceAttr) {
+                addString(attrToFlatten.xmlAttr->name, kLowPriority, &flatAttr->name);
+            } else {
+                // We've already extracted the package successfully before.
+                assert(package);
+
+                // Attribute names are stored without packages, but we use
+                // their StringPool index to lookup their resource IDs.
+                // This will cause collisions, so we can't dedupe
+                // attribute names from different packages. We use separate
+                // pools that we later combine.
+                //
+                // Lookup the StringPool for this package and make the reference there.
+                StringPool::Ref nameRef = (*mPackagePools)[package.value()].makeRef(
+                        attrToFlatten.xmlAttr->name,
+                        StringPool::Context{ attrToFlatten.resourceId });
+
+                // Add it to the list of strings to flatten.
+                addString(nameRef, &flatAttr->name);
+
+                if (mOptions.keepRawValues) {
+                    // Keep raw values (this is for static libraries).
+                    // TODO(with a smarter inflater for binary XML, we can do without this).
+                    addString(attrToFlatten.xmlAttr->value, kLowPriority, &flatAttr->rawValue);
+                }
+            }
+
+            error |= !flattenItem(node, attrToFlatten.xmlAttr->value, attrToFlatten.resourceAttr,
+                                  flatAttr);
+            flatAttr->typedValue.size = sizeof(flatAttr->typedValue);
+            flatAttr++;
+        }
+        return !error;
+    }
+
+    Maybe<size_t> getSmallestFilteredSdk() const {
+        if (mSmallestFilteredSdk == std::numeric_limits<size_t>::max()) {
+            return {};
+        }
+        return mSmallestFilteredSdk;
+    }
+
+private:
+    bool flattenItem(const Node* el, const std::u16string& value, const ::aapt::Attribute* attr,
+                     android::ResXMLTree_attribute* flatAttr) {
+        std::unique_ptr<Item> item;
+        if (!attr) {
+            bool create = false;
+            item = ResourceParser::tryParseReference(value, &create);
+            if (!item) {
+                flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
+                addString(value, kLowPriority, &flatAttr->rawValue);
+                addString(value, kLowPriority, reinterpret_cast<android::ResStringPool_ref*>(
+                        &flatAttr->typedValue.data));
+                return true;
+            }
+        } else {
+            item = ResourceParser::parseItemForAttribute(value, *attr);
+            if (!item) {
+                if (!(attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+                    mLogger->error(el->lineNumber)
+                            << "'"
+                            << value
+                            << "' is not compatible with attribute '"
+                            << *attr
+                            << "'."
+                            << std::endl;
+                    return false;
+                }
+
+                flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
+                addString(value, kLowPriority, &flatAttr->rawValue);
+                addString(value, kLowPriority, reinterpret_cast<android::ResStringPool_ref*>(
+                        &flatAttr->typedValue.data));
+                return true;
+            }
+        }
+
+        assert(item);
+
+        bool error = false;
+
+        // If this is a reference, resolve the name into an ID.
+        visitFunc<Reference>(*item, [&](Reference& reference) {
+            // First see if we can convert the package name from a prefix to a real
+            // package name.
+            ResourceName realName = reference.name;
+            if (!realName.package.empty()) {
+                Maybe<std::u16string> package = getPackageAlias(realName.package);
+                if (package) {
+                    realName.package = package.value();
+                }
+            } else {
+                realName.package = getDefaultPackage();
+            }
+
+            Maybe<ResourceId> result = mResolver->findId(realName);
+            if (!result || !result.value().isValid()) {
+                std::ostream& out = mLogger->error(el->lineNumber)
+                        << "unresolved reference '"
+                        << reference.name
+                        << "'";
+                if (realName != reference.name) {
+                    out << " (aka '" << realName << "')";
+                }
+                out << "'." << std::endl;
+                error = true;
+            } else {
+                reference.id = result.value();
+            }
+        });
+
+        if (error) {
+            return false;
+        }
+
+        item->flatten(flatAttr->typedValue);
+        return true;
+    }
+
+    std::shared_ptr<IResolver> mResolver;
+    SourceLogger* mLogger;
+    std::map<std::u16string, StringPool>* mPackagePools;
+    FlattenOptions mOptions;
+    size_t mSmallestFilteredSdk = std::numeric_limits<size_t>::max();
+};
 
 /**
- * Reads events from the parser and writes to a BigBuffer. The binary XML file
- * expects the StringPool to appear first, but we haven't collected the strings yet. We
- * write to a temporary BigBuffer while parsing the input, adding strings we encounter
- * to the StringPool. At the end, we write the StringPool to the given BigBuffer and
+ * The binary XML file expects the StringPool to appear first, but we haven't collected the
+ * strings yet. We write to a temporary BigBuffer while parsing the input, adding strings
+ * we encounter to the StringPool. At the end, we write the StringPool to the given BigBuffer and
  * then move the data from the temporary BigBuffer into the given one. This incurs no
  * copies as the given BigBuffer simply takes ownership of the data.
  */
-Maybe<size_t> XmlFlattener::flatten(const Source& source,
-                                    const std::shared_ptr<XmlPullParser>& parser,
-                                    BigBuffer* outBuffer, Options options) {
-    SourceLogger logger(source);
-    StringPool pool;
-    bool error = false;
-
-    size_t smallestStrippedAttributeSdk = std::numeric_limits<size_t>::max();
-
-    // Attribute names are stored without packages, but we use
-    // their StringPool index to lookup their resource IDs.
-    // This will cause collisions, so we can't dedupe
-    // attribute names from different packages. We use separate
-    // pools that we later combine.
-    std::map<std::u16string, StringPool> packagePools;
-
-    // Attribute resource IDs are stored in the same order
-    // as the attribute names appear in the StringPool.
-    // Since the StringPool contains more than just attribute
-    // names, to maintain a tight packing of resource IDs,
-    // we must ensure that attribute names appear first
-    // in our StringPool. For this, we assign a low priority
-    // (0xffffffff) to non-attribute strings. Attribute
-    // names will be stored along with a priority equal
-    // to their resource ID so that they are ordered.
-    StringPool::Context lowPriority { 0xffffffffu };
-
-    // Once we sort the StringPool, we can assign the updated indices
-    // to the correct data locations.
-    std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>> stringRefs;
-
-    // Since we don't know the size of the final StringPool, we write to this
-    // temporary BigBuffer, which we will append to outBuffer later.
-    BigBuffer out(1024);
-    while (XmlPullParser::isGoodEvent(parser->next())) {
-        XmlPullParser::Event event = parser->getEvent();
-        switch (event) {
-            case XmlPullParser::Event::kStartNamespace:
-            case XmlPullParser::Event::kEndNamespace: {
-                const size_t startIndex = out.size();
-                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
-                if (event == XmlPullParser::Event::kStartNamespace) {
-                    node->header.type = android::RES_XML_START_NAMESPACE_TYPE;
-                } else {
-                    node->header.type = android::RES_XML_END_NAMESPACE_TYPE;
-                }
-
-                node->header.headerSize = sizeof(*node);
-                node->lineNumber = parser->getLineNumber();
-                node->comment.index = -1;
-
-                android::ResXMLTree_namespaceExt* ns =
-                        out.nextBlock<android::ResXMLTree_namespaceExt>();
-                stringRefs.emplace_back(
-                        pool.makeRef(parser->getNamespacePrefix(), lowPriority), &ns->prefix);
-                stringRefs.emplace_back(
-                        pool.makeRef(parser->getNamespaceUri(), lowPriority), &ns->uri);
-
-                out.align4();
-                node->header.size = out.size() - startIndex;
-                break;
-            }
-
-            case XmlPullParser::Event::kStartElement: {
-                const size_t startIndex = out.size();
-                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
-                node->header.type = android::RES_XML_START_ELEMENT_TYPE;
-                node->header.headerSize = sizeof(*node);
-                node->lineNumber = parser->getLineNumber();
-                node->comment.index = -1;
-
-                android::ResXMLTree_attrExt* elem = out.nextBlock<android::ResXMLTree_attrExt>();
-                if (!parser->getElementNamespace().empty()) {
-                    stringRefs.emplace_back(
-                            pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
-                } else {
-                    // The device doesn't think a string of size 0 is the same as null.
-                    elem->ns.index = -1;
-                }
-                stringRefs.emplace_back(
-                        pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
-                elem->attributeStart = sizeof(*elem);
-                elem->attributeSize = sizeof(android::ResXMLTree_attribute);
-
-                // The resource system expects attributes to be sorted by resource ID.
-                std::vector<XmlAttribute> sortedAttributes;
-                uint32_t nextAttributeId = 0;
-                const auto endAttrIter = parser->endAttributes();
-                for (auto attrIter = parser->beginAttributes();
-                        attrIter != endAttrIter;
-                        ++attrIter) {
-                    uint32_t id;
-                    StringPool::Ref nameRef;
-                    const Attribute* attr = nullptr;
-
-                    if (options.maxSdkAttribute && attrIter->namespaceUri == kSchemaAndroid) {
-                        size_t sdkVersion = findAttributeSdkLevel(attrIter->name);
-                        if (sdkVersion > options.maxSdkAttribute.value()) {
-                            // We will silently omit this attribute
-                            smallestStrippedAttributeSdk =
-                                    std::min(smallestStrippedAttributeSdk, sdkVersion);
-                            continue;
-                        }
-                    }
-
-                    ResourceNameRef genIdName;
-                    bool create = false;
-                    bool privateRef = false;
-                    if (mTable && ResourceParser::tryParseReference(attrIter->value, &genIdName,
-                            &create, &privateRef) && create) {
-                        mTable->addResource(genIdName, {}, source.line(parser->getLineNumber()),
-                                            util::make_unique<Id>());
-                    }
-
-
-                    Maybe<std::u16string> package = util::extractPackageFromNamespace(
-                            attrIter->namespaceUri);
-                    if (!package || !mResolver) {
-                        // Attributes that have no resource ID (because they don't belong to a
-                        // package) should appear after those that do have resource IDs. Assign
-                        // them some integer value that will appear after.
-                        id = 0x80000000u | nextAttributeId++;
-                        nameRef = pool.makeRef(attrIter->name, StringPool::Context{ id });
-
-                    } else {
-                        // Find the Attribute object via our Resolver.
-                        ResourceName attrName = {
-                                package.value(), ResourceType::kAttr, attrIter->name };
-
-                        if (attrName.package.empty()) {
-                            attrName.package = options.defaultPackage;
-                        }
-
-                        Maybe<IResolver::Entry> result = mResolver->findAttribute(attrName);
-                        if (!result || !result.value().id.isValid()) {
-                            logger.error(parser->getLineNumber())
-                                    << "unresolved attribute '"
-                                    << attrName
-                                    << "'."
-                                    << std::endl;
-                            error = true;
-                            continue;
-                        }
-
-                        if (!result.value().attr) {
-                            logger.error(parser->getLineNumber())
-                                    << "not a valid attribute '"
-                                    << attrName
-                                    << "'."
-                                    << std::endl;
-                            error = true;
-                            continue;
-                        }
-
-                        id = result.value().id.id;
-                        attr = result.value().attr;
-
-                        // Put the attribute name into a package specific pool, since we don't
-                        // want to collapse names from different packages.
-                        nameRef = packagePools[package.value()].makeRef(
-                                attrIter->name, StringPool::Context{ id });
-                    }
-
-                    // Insert the attribute into the sorted vector.
-                    auto iter = std::lower_bound(sortedAttributes.begin(), sortedAttributes.end(),
-                                                 id, lessAttributeId);
-                    sortedAttributes.insert(iter, XmlAttribute{ id, &*attrIter, attr, nameRef });
-                }
-
-                if (error) {
-                    break;
-                }
-
-                // Now that we have filtered out some attributes, get the final count.
-                elem->attributeCount = sortedAttributes.size();
-
-                // Flatten the sorted attributes.
-                uint16_t attributeIndex = 1;
-                for (auto entry : sortedAttributes) {
-                    android::ResXMLTree_attribute* attr =
-                            out.nextBlock<android::ResXMLTree_attribute>();
-                    if (!entry.xmlAttr->namespaceUri.empty()) {
-                        stringRefs.emplace_back(
-                                pool.makeRef(entry.xmlAttr->namespaceUri, lowPriority), &attr->ns);
-                    } else {
-                        attr->ns.index = -1;
-                    }
-
-                    StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
-
-                    stringRefs.emplace_back(entry.nameRef, &attr->name);
-
-                    if (options.keepRawValues) {
-                        stringRefs.emplace_back(rawValueRef, &attr->rawValue);
-                    } else {
-                        attr->rawValue.index = -1;
-                    }
-
-                    // Assign the indices for specific attributes.
-                    if (entry.xmlAttr->namespaceUri == kSchemaAndroid &&
-                            entry.xmlAttr->name == u"id") {
-                        elem->idIndex = attributeIndex;
-                    } else if (entry.xmlAttr->namespaceUri.empty()) {
-                        if (entry.xmlAttr->name == u"class") {
-                            elem->classIndex = attributeIndex;
-                        } else if (entry.xmlAttr->name == u"style") {
-                            elem->styleIndex = attributeIndex;
-                        }
-                    }
-                    attributeIndex++;
-
-                    std::unique_ptr<Item> value;
-                    if (entry.attr) {
-                        value = ResourceParser::parseItemForAttribute(entry.xmlAttr->value,
-                                                                      *entry.attr);
-                    } else {
-                        bool create = false;
-                        value = ResourceParser::tryParseReference(entry.xmlAttr->value, &create);
-                    }
-
-                    if (mResolver && value) {
-                        AttributeValueFlattener flattener(
-                                mResolver,
-                                &logger,
-                                &attr->typedValue,
-                                parser,
-                                &error,
-                                rawValueRef,
-                                &options.defaultPackage,
-                                &stringRefs);
-                        value->accept(flattener, {});
-                    } else if (!value && entry.attr &&
-                            !(entry.attr->typeMask & android::ResTable_map::TYPE_STRING)) {
-                        logger.error(parser->getLineNumber())
-                                << "'"
-                                << *rawValueRef
-                                << "' is not compatible with attribute "
-                                << *entry.attr
-                                << "."
-                                << std::endl;
-                        error = true;
-                    } else {
-                        attr->typedValue.dataType = android::Res_value::TYPE_STRING;
-                        if (!options.keepRawValues) {
-                            // Don't set the string twice.
-                            stringRefs.emplace_back(rawValueRef, &attr->rawValue);
-                        }
-                        stringRefs.emplace_back(rawValueRef,
-                                reinterpret_cast<android::ResStringPool_ref*>(
-                                        &attr->typedValue.data));
-                    }
-                    attr->typedValue.size = sizeof(attr->typedValue);
-                }
-
-                out.align4();
-                node->header.size = out.size() - startIndex;
-                break;
-            }
-
-            case XmlPullParser::Event::kEndElement: {
-                const size_t startIndex = out.size();
-                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
-                node->header.type = android::RES_XML_END_ELEMENT_TYPE;
-                node->header.headerSize = sizeof(*node);
-                node->lineNumber = parser->getLineNumber();
-                node->comment.index = -1;
-
-                android::ResXMLTree_endElementExt* elem =
-                        out.nextBlock<android::ResXMLTree_endElementExt>();
-                stringRefs.emplace_back(
-                        pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
-                stringRefs.emplace_back(
-                        pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
-
-                out.align4();
-                node->header.size = out.size() - startIndex;
-                break;
-            }
-
-            case XmlPullParser::Event::kText: {
-                StringPiece16 text = util::trimWhitespace(parser->getText());
-                if (text.empty()) {
-                    break;
-                }
-
-                const size_t startIndex = out.size();
-                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
-                node->header.type = android::RES_XML_CDATA_TYPE;
-                node->header.headerSize = sizeof(*node);
-                node->lineNumber = parser->getLineNumber();
-                node->comment.index = -1;
-
-                android::ResXMLTree_cdataExt* elem = out.nextBlock<android::ResXMLTree_cdataExt>();
-                stringRefs.emplace_back(pool.makeRef(text, lowPriority), &elem->data);
-
-                out.align4();
-                node->header.size = out.size() - startIndex;
-                break;
-            }
-
-            default:
-                break;
-        }
-
-    }
-    out.align4();
-
-    if (error) {
-        return {};
-    }
-
-    if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
-        logger.error(parser->getLineNumber())
-                << parser->getLastError()
-                << std::endl;
-        return {};
-    }
-
-    // Merge the package pools into the main pool.
-    for (auto& packagePoolEntry : packagePools) {
-        pool.merge(std::move(packagePoolEntry.second));
-    }
-
-    // Sort so that attribute resource IDs show up first.
-    pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+static void flattenXml(StringPool* pool, FlatStringRefList* stringRefs, BigBuffer* outBuffer,
+                       BigBuffer&& xmlTreeBuffer) {
+    // Sort the string pool so that attribute resource IDs show up first.
+    pool->sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
         return a.context.priority < b.context.priority;
     });
 
     // Now we flatten the string pool references into the correct places.
-    for (const auto& refEntry : stringRefs) {
+    for (const auto& refEntry : *stringRefs) {
         refEntry.second->index = refEntry.first.getIndex();
     }
 
@@ -463,36 +482,93 @@
     header->header.headerSize = sizeof(*header);
 
     // Flatten the StringPool.
-    StringPool::flattenUtf16(outBuffer, pool);
+    StringPool::flattenUtf16(outBuffer, *pool);
 
     // Write the array of resource IDs, indexed by StringPool order.
     const size_t beforeResIdMapIndex = outBuffer->size();
     android::ResChunk_header* resIdMapChunk = outBuffer->nextBlock<android::ResChunk_header>();
     resIdMapChunk->type = android::RES_XML_RESOURCE_MAP_TYPE;
     resIdMapChunk->headerSize = sizeof(*resIdMapChunk);
-    for (const auto& str : pool) {
+    for (const auto& str : *pool) {
         ResourceId id { str->context.priority };
-        if (!id.isValid()) {
+        if (id.id == kLowPriority || !id.isValid()) {
             // When we see the first non-resource ID,
             // we're done.
             break;
         }
 
-        uint32_t* flatId = outBuffer->nextBlock<uint32_t>();
-        *flatId = id.id;
+        *outBuffer->nextBlock<uint32_t>() = id.id;
     }
     resIdMapChunk->size = outBuffer->size() - beforeResIdMapIndex;
 
     // Move the temporary BigBuffer into outBuffer.
-    outBuffer->appendBuffer(std::move(out));
-
+    outBuffer->appendBuffer(std::move(xmlTreeBuffer));
     header->header.size = outBuffer->size() - beforeXmlTreeIndex;
-
-    if (smallestStrippedAttributeSdk == std::numeric_limits<size_t>::max()) {
-        // Nothing was stripped
-        return 0u;
-    }
-    return smallestStrippedAttributeSdk;
 }
 
+bool flatten(Node* root, const std::u16string& defaultPackage, BigBuffer* outBuffer) {
+    StringPool pool;
+
+    // This will hold the StringRefs and the location in which to write the index.
+    // Once we sort the StringPool, we can assign the updated indices
+    // to the correct data locations.
+    FlatStringRefList stringRefs;
+
+    // Since we don't know the size of the final StringPool, we write to this
+    // temporary BigBuffer, which we will append to outBuffer later.
+    BigBuffer out(1024);
+
+    CompileXmlFlattener flattener(&out, &pool, &stringRefs, defaultPackage);
+    root->accept(&flattener);
+
+    if (!flattener.success()) {
+        return false;
+    }
+
+    flattenXml(&pool, &stringRefs, outBuffer, std::move(out));
+    return true;
+};
+
+Maybe<size_t> flattenAndLink(const Source& source, Node* root,
+                             const std::u16string& defaultPackage,
+                             const std::shared_ptr<IResolver>& resolver,
+                             const FlattenOptions& options, BigBuffer* outBuffer) {
+    SourceLogger logger(source);
+    StringPool pool;
+
+    // Attribute names are stored without packages, but we use
+    // their StringPool index to lookup their resource IDs.
+    // This will cause collisions, so we can't dedupe
+    // attribute names from different packages. We use separate
+    // pools that we later combine.
+    std::map<std::u16string, StringPool> packagePools;
+
+    FlatStringRefList stringRefs;
+
+    // Since we don't know the size of the final StringPool, we write to this
+    // temporary BigBuffer, which we will append to outBuffer later.
+    BigBuffer out(1024);
+
+    LinkedXmlFlattener flattener(&out, &pool, &packagePools, &stringRefs, defaultPackage, resolver,
+                                 &logger, options);
+    root->accept(&flattener);
+
+    if (!flattener.success()) {
+        return {};
+    }
+
+    // Merge the package pools into the main pool.
+    for (auto& packagePoolEntry : packagePools) {
+        pool.merge(std::move(packagePoolEntry.second));
+    }
+
+    flattenXml(&pool, &stringRefs, outBuffer, std::move(out));
+
+    if (flattener.getSmallestFilteredSdk()) {
+        return flattener.getSmallestFilteredSdk();
+    }
+    return 0;
+}
+
+} // namespace xml
 } // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.h b/tools/aapt2/XmlFlattener.h
index 2cfcc16..4ece0a3 100644
--- a/tools/aapt2/XmlFlattener.h
+++ b/tools/aapt2/XmlFlattener.h
@@ -21,64 +21,49 @@
 #include "Maybe.h"
 #include "Resolver.h"
 #include "Source.h"
-#include "XmlPullParser.h"
+#include "XmlDom.h"
 
 #include <string>
 
 namespace aapt {
+namespace xml {
 
 /**
  * Flattens an XML file into a binary representation parseable by
- * the Android resource system. References to resources are checked
- * and string values are transformed to typed data where possible.
+ * the Android resource system.
  */
-class XmlFlattener {
-public:
-    struct Options {
-        /**
-         * The package to use when a reference has no package specified
-         * (or a namespace URI equals "http://schemas.android.com/apk/res-auto").
-         */
-        std::u16string defaultPackage;
+bool flatten(Node* root, const std::u16string& defaultPackage, BigBuffer* outBuffer);
 
-        /**
-         * If set, tells the XmlFlattener to strip out
-         * attributes that have been introduced after
-         * max SDK.
-         */
-        Maybe<size_t> maxSdkAttribute;
-
-        /**
-         * Setting this to true will keep the raw string value of
-         * an attribute's value when it has been resolved.
-         */
-        bool keepRawValues = false;
-    };
+/**
+ * Options for flattenAndLink.
+ */
+struct FlattenOptions {
+    /**
+     * Keep attribute raw string values along with typed values.
+     */
+    bool keepRawValues = false;
 
     /**
-     * Creates a flattener with a Resolver to resolve references
-     * and attributes.
+     * If set, any attribute introduced in a later SDK will not be encoded.
      */
-    XmlFlattener(const std::shared_ptr<ResourceTable>& table,
-                 const std::shared_ptr<IResolver>& resolver);
-
-    XmlFlattener(const XmlFlattener&) = delete; // Not copyable.
-
-    /**
-     * Flatten an XML file, reading from the XML parser and writing to the
-     * BigBuffer. The source object is mainly for logging errors. If the
-     * function succeeds, returns the smallest SDK version of an attribute that
-     * was stripped out. If no attributes were stripped out, the return value
-     * is 0.
-     */
-    Maybe<size_t> flatten(const Source& source, const std::shared_ptr<XmlPullParser>& parser,
-                          BigBuffer* outBuffer, Options options);
-
-private:
-    std::shared_ptr<ResourceTable> mTable;
-    std::shared_ptr<IResolver> mResolver;
+    Maybe<size_t> maxSdkAttribute;
 };
 
+/**
+ * Like flatten(Node*,BigBuffer*), but references to resources are checked
+ * and string values are transformed to typed data where possible.
+ *
+ * `defaultPackage` is used when a reference has no package or the namespace URI
+ * "http://schemas.android.com/apk/res-auto" is used.
+ *
+ * `resolver` is used to resolve references to resources.
+ */
+Maybe<size_t> flattenAndLink(const Source& source, Node* root,
+                             const std::u16string& defaultPackage,
+                             const std::shared_ptr<IResolver>& resolver,
+                             const FlattenOptions& options, BigBuffer* outBuffer);
+
+} // namespace xml
 } // namespace aapt
 
 #endif // AAPT_XML_FLATTENER_H
diff --git a/tools/aapt2/XmlFlattener_test.cpp b/tools/aapt2/XmlFlattener_test.cpp
index b45cd9b..8915d24 100644
--- a/tools/aapt2/XmlFlattener_test.cpp
+++ b/tools/aapt2/XmlFlattener_test.cpp
@@ -17,7 +17,6 @@
 #include "MockResolver.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
-#include "SourceXmlPullParser.h"
 #include "Util.h"
 #include "XmlFlattener.h"
 
@@ -30,13 +29,14 @@
 using namespace android;
 
 namespace aapt {
+namespace xml {
 
 constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
 
 class XmlFlattenerTest : public ::testing::Test {
 public:
     virtual void SetUp() override {
-        std::shared_ptr<IResolver> resolver = std::make_shared<MockResolver>(
+        mResolver = std::make_shared<MockResolver>(
                 std::make_shared<ResourceTable>(),
                 std::map<ResourceName, ResourceId>({
                         { ResourceName{ u"android", ResourceType::kAttr, u"attr" },
@@ -47,18 +47,21 @@
                           ResourceId{ 0x01010001u } },
                         { ResourceName{ u"com.lib", ResourceType::kId, u"id" },
                           ResourceId{ 0x01020001u } }}));
-
-        mFlattener = std::make_shared<XmlFlattener>(nullptr, resolver);
     }
 
     ::testing::AssertionResult testFlatten(const std::string& in, ResXMLTree* outTree) {
         std::stringstream input(kXmlPreamble);
         input << in << std::endl;
-        std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
+
+        SourceLogger logger(Source{ "test.xml" });
+        std::unique_ptr<Node> root = inflate(&input, &logger);
+        if (!root) {
+            return ::testing::AssertionFailure();
+        }
+
         BigBuffer outBuffer(1024);
-        XmlFlattener::Options xmlOptions;
-        xmlOptions.defaultPackage = u"android";
-        if (!mFlattener->flatten(Source{ "test" }, xmlParser, &outBuffer, xmlOptions)) {
+        if (!flattenAndLink(Source{ "test.xml" }, root.get(), std::u16string(u"android"),
+                    mResolver, {}, &outBuffer)) {
             return ::testing::AssertionFailure();
         }
 
@@ -69,16 +72,48 @@
         return ::testing::AssertionSuccess();
     }
 
-    std::shared_ptr<XmlFlattener> mFlattener;
+    std::shared_ptr<IResolver> mResolver;
 };
 
 TEST_F(XmlFlattenerTest, ParseSimpleView) {
-    std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
-                        "      android:attr=\"@id/id\">\n"
-                        "</View>";
+    std::string input = R"EOF(
+        <View xmlns:android="http://schemas.android.com/apk/res/android"
+              android:attr="@id/id"
+              class="str"
+              style="@id/id">
+        </View>
+    )EOF";
     ResXMLTree tree;
     ASSERT_TRUE(testFlatten(input, &tree));
 
+    while (tree.next() != ResXMLTree::START_TAG) {
+        ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
+        ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
+    }
+
+    const StringPiece16 androidNs = u"http://schemas.android.com/apk/res/android";
+    const StringPiece16 attrName = u"attr";
+    ssize_t idx = tree.indexOfAttribute(androidNs.data(), androidNs.size(), attrName.data(),
+                                        attrName.size());
+    ASSERT_GE(idx, 0);
+    EXPECT_EQ(tree.getAttributeNameResID(idx), 0x01010000u);
+    EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE);
+
+    const StringPiece16 class16 = u"class";
+    idx = tree.indexOfAttribute(nullptr, 0, class16.data(), class16.size());
+    ASSERT_GE(idx, 0);
+    EXPECT_EQ(tree.getAttributeNameResID(idx), 0u);
+    EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_STRING);
+    EXPECT_EQ(tree.getAttributeData(idx), tree.getAttributeValueStringID(idx));
+
+    const StringPiece16 style16 = u"style";
+    idx = tree.indexOfAttribute(nullptr, 0, style16.data(), style16.size());
+    ASSERT_GE(idx, 0);
+    EXPECT_EQ(tree.getAttributeNameResID(idx), 0u);
+    EXPECT_EQ(tree.getAttributeDataType(idx), android::Res_value::TYPE_REFERENCE);
+    EXPECT_EQ((uint32_t) tree.getAttributeData(idx), 0x01020000u);
+    EXPECT_EQ(tree.getAttributeValueStringID(idx), -1);
+
     while (tree.next() != ResXMLTree::END_DOCUMENT) {
         ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
     }
@@ -193,4 +228,5 @@
     EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
 }
 
+} // namespace xml
 } // namespace aapt
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 5f430f0..a3dc077 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1007,7 +1007,7 @@
                             ((ParcelableScanResults) msg.obj).getResults());
                     return;
                 case CMD_SINGLE_SCAN_COMPLETED:
-                    Log.d(TAG, "removing listener for single scan");
+                    if (DBG) Log.d(TAG, "removing listener for single scan");
                     removeListener(msg.arg2);
                     break;
                 default: