Merge "Player activity notification: SoundPool not ready for activity reporting"
diff --git a/Android.mk b/Android.mk
index 9ebc276..28adbca 100644
--- a/Android.mk
+++ b/Android.mk
@@ -264,8 +264,6 @@
 	core/java/android/os/storage/IObbActionListener.aidl \
 	core/java/android/security/IKeystoreService.aidl \
 	core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \
-	core/java/android/service/autofill/IAutoFillAppCallback.aidl \
-	core/java/android/service/autofill/IAutoFillManagerService.aidl \
 	core/java/android/service/autofill/IAutoFillService.aidl \
 	core/java/android/service/autofill/IFillCallback.aidl \
 	core/java/android/service/autofill/ISaveCallback.aidl \
@@ -318,6 +316,8 @@
 	core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
+	core/java/android/view/autofill/IAutoFillManager.aidl \
+	core/java/android/view/autofill/IAutoFillManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
 	core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
 	core/java/android/view/IDockedStackListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index a2041ef..decd819 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -601,6 +601,8 @@
     field public static final int font = 16844082; // 0x1010532
     field public static final int fontFamily = 16843692; // 0x10103ac
     field public static final int fontFeatureSettings = 16843959; // 0x10104b7
+    field public static final int fontProviderAuthority = 16844114; // 0x1010552
+    field public static final int fontProviderQuery = 16844115; // 0x1010553
     field public static final int fontStyle = 16844081; // 0x1010531
     field public static final int fontWeight = 16844083; // 0x1010533
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -9182,10 +9184,6 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
-    field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
-    field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
-    field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
-    field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -9927,6 +9925,15 @@
     method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
   }
 
+  public final class ChangedPackages implements android.os.Parcelable {
+    ctor public ChangedPackages(int, java.util.List<java.lang.String>);
+    method public int describeContents();
+    method public java.util.List<java.lang.String> getPackageNames();
+    method public int getSequenceNumber();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR;
+  }
+
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
     ctor public ComponentInfo();
     ctor public ComponentInfo(android.content.pm.ComponentInfo);
@@ -10273,6 +10280,7 @@
     method public abstract java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.ChangedPackages getChangedPackages(int);
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -12381,14 +12389,14 @@
     method public void rotate(float);
     method public final void rotate(float, float, float);
     method public int save();
-    method public int save(int);
-    method public int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
+    method public deprecated int save(int);
+    method public deprecated int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
     method public int saveLayer(android.graphics.RectF, android.graphics.Paint);
-    method public int saveLayer(float, float, float, float, android.graphics.Paint, int);
+    method public deprecated int saveLayer(float, float, float, float, android.graphics.Paint, int);
     method public int saveLayer(float, float, float, float, android.graphics.Paint);
-    method public int saveLayerAlpha(android.graphics.RectF, int, int);
+    method public deprecated int saveLayerAlpha(android.graphics.RectF, int, int);
     method public int saveLayerAlpha(android.graphics.RectF, int);
-    method public int saveLayerAlpha(float, float, float, float, int, int);
+    method public deprecated int saveLayerAlpha(float, float, float, float, int, int);
     method public int saveLayerAlpha(float, float, float, float, int);
     method public void scale(float, float);
     method public final void scale(float, float, float, float);
@@ -12399,11 +12407,11 @@
     method public void skew(float, float);
     method public void translate(float, float);
     field public static final int ALL_SAVE_FLAG = 31; // 0x1f
-    field public static final int CLIP_SAVE_FLAG = 2; // 0x2
-    field public static final int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
-    field public static final int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
-    field public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
-    field public static final int MATRIX_SAVE_FLAG = 1; // 0x1
+    field public static final deprecated int CLIP_SAVE_FLAG = 2; // 0x2
+    field public static final deprecated int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
+    field public static final deprecated int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
+    field public static final deprecated int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
+    field public static final deprecated int MATRIX_SAVE_FLAG = 1; // 0x1
   }
 
   public static final class Canvas.EdgeType extends java.lang.Enum {
@@ -13780,6 +13788,7 @@
     method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
     method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
     method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
+    method public static android.graphics.drawable.Icon createWithMaskableBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
     method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
     method public int describeContents();
@@ -13863,9 +13872,9 @@
   }
 
   public class MaskableIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
-    ctor public MaskableIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable getBackground();
+    method public static float getExtraInsetPercentage();
     method public android.graphics.drawable.Drawable getForeground();
     method public android.graphics.Path getIconMask();
     method public int getOpacity();
@@ -13875,8 +13884,6 @@
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void setOpacity(int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
-    field public static final float DEFAULT_VIEW_PORT_SCALE = 0.6666667f;
-    field public static final float MASK_SIZE = 100.0f;
   }
 
   public class NinePatchDrawable extends android.graphics.drawable.Drawable {
@@ -14435,6 +14442,7 @@
     field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
     field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light";
     field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration";
+    field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
     field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
@@ -14463,6 +14471,7 @@
     field public static final int TYPE_HEART_RATE = 21; // 0x15
     field public static final int TYPE_LIGHT = 5; // 0x5
     field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
+    field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final int TYPE_MOTION_DETECT = 30; // 0x1e
@@ -36205,12 +36214,37 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class FillCallback implements android.os.Parcelable {
+  public final class Dataset implements android.os.Parcelable {
     method public int describeContents();
-    method public void onFailure(java.lang.CharSequence);
-    method public void onSuccess(android.view.autofill.FillResponse);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.service.autofill.Dataset> CREATOR;
+  }
+
+  public static final class Dataset.Builder {
+    ctor public Dataset.Builder(java.lang.CharSequence);
+    method public android.service.autofill.Dataset build();
+    method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
+  }
+
+  public final class FillCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(android.service.autofill.FillResponse);
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR;
+  }
+
+  public static final class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
+    method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
+    method public android.service.autofill.FillResponse build();
+    method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
   public final class SaveCallback {
@@ -40041,6 +40075,7 @@
     method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.ChangedPackages getChangedPackages(int);
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -46855,6 +46890,8 @@
     method public void valueChanged(android.view.View);
     method public void virtualFocusChanged(android.view.View, int, android.graphics.Rect, boolean);
     method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
+    field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
+    field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
   }
 
   public final class AutoFillType implements android.os.Parcelable {
@@ -46882,35 +46919,6 @@
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR;
   }
 
-  public final class Dataset implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR;
-  }
-
-  public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
-    method public android.view.autofill.Dataset build();
-    method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
-    method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
-    method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
-  }
-
-  public final class FillResponse implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR;
-  }
-
-  public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder(java.lang.String);
-    method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
-    method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
-    method public android.view.autofill.FillResponse build();
-    method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
-    method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
-  }
-
 }
 
 package android.view.inputmethod {
@@ -47321,7 +47329,7 @@
 
   public final class TextClassificationManager {
     method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+    method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
   }
 
   public final class TextClassificationResult {
@@ -52455,6 +52463,13 @@
     field public static final java.lang.Class<java.lang.Boolean> TYPE;
   }
 
+  public class BootstrapMethodError extends java.lang.LinkageError {
+    ctor public BootstrapMethodError();
+    ctor public BootstrapMethodError(java.lang.String);
+    ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable);
+    ctor public BootstrapMethodError(java.lang.Throwable);
+  }
+
   public final class Byte extends java.lang.Number implements java.lang.Comparable {
     ctor public Byte(byte);
     ctor public Byte(java.lang.String) throws java.lang.NumberFormatException;
@@ -54341,6 +54356,21 @@
 
 package java.lang.invoke {
 
+  public abstract class CallSite {
+    method public abstract java.lang.invoke.MethodHandle dynamicInvoker();
+    method public abstract java.lang.invoke.MethodHandle getTarget();
+    method public abstract void setTarget(java.lang.invoke.MethodHandle);
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public class ConstantCallSite extends java.lang.invoke.CallSite {
+    ctor public ConstantCallSite(java.lang.invoke.MethodHandle);
+    ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable;
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public final void setTarget(java.lang.invoke.MethodHandle);
+  }
+
   public class LambdaConversionException extends java.lang.Exception {
     ctor public LambdaConversionException();
     ctor public LambdaConversionException(java.lang.String);
@@ -54469,6 +54499,22 @@
     method public java.lang.invoke.MethodType wrap();
   }
 
+  public class MutableCallSite extends java.lang.invoke.CallSite {
+    ctor public MutableCallSite(java.lang.invoke.MethodType);
+    ctor public MutableCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
+  public class VolatileCallSite extends java.lang.invoke.CallSite {
+    ctor public VolatileCallSite(java.lang.invoke.MethodType);
+    ctor public VolatileCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
   public class WrongMethodTypeException extends java.lang.RuntimeException {
     ctor public WrongMethodTypeException();
     ctor public WrongMethodTypeException(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 452f195..bfb6733 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -713,6 +713,8 @@
     field public static final int font = 16844082; // 0x1010532
     field public static final int fontFamily = 16843692; // 0x10103ac
     field public static final int fontFeatureSettings = 16843959; // 0x10104b7
+    field public static final int fontProviderAuthority = 16844114; // 0x1010552
+    field public static final int fontProviderQuery = 16844115; // 0x1010553
     field public static final int fontStyle = 16844081; // 0x1010531
     field public static final int fontWeight = 16844083; // 0x1010533
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -6892,6 +6894,7 @@
     ctor public BackupManager(android.content.Context);
     method public void backupNow();
     method public android.app.backup.RestoreSession beginRestoreSession();
+    method public void cancelBackups();
     method public void dataChanged();
     method public static void dataChanged(java.lang.String);
     method public long getAvailableRestoreToken(java.lang.String);
@@ -6908,6 +6911,7 @@
     method public void setAutoRestore(boolean);
     method public void setBackupEnabled(boolean);
     field public static final int ERROR_AGENT_FAILURE = -1003; // 0xfffffc15
+    field public static final int ERROR_BACKUP_CANCELLED = -2003; // 0xfffff82d
     field public static final int ERROR_BACKUP_NOT_ALLOWED = -2001; // 0xfffff82f
     field public static final int ERROR_PACKAGE_NOT_FOUND = -2002; // 0xfffff82e
     field public static final int ERROR_TRANSPORT_ABORTED = -1000; // 0xfffffc18
@@ -9620,10 +9624,6 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
-    field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
-    field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
-    field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
-    field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -10373,6 +10373,15 @@
     method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
   }
 
+  public final class ChangedPackages implements android.os.Parcelable {
+    ctor public ChangedPackages(int, java.util.List<java.lang.String>);
+    method public int describeContents();
+    method public java.util.List<java.lang.String> getPackageNames();
+    method public int getSequenceNumber();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR;
+  }
+
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
     ctor public ComponentInfo();
     ctor public ComponentInfo(android.content.pm.ComponentInfo);
@@ -10770,6 +10779,7 @@
     method public abstract java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.ChangedPackages getChangedPackages(int);
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -12970,14 +12980,14 @@
     method public void rotate(float);
     method public final void rotate(float, float, float);
     method public int save();
-    method public int save(int);
-    method public int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
+    method public deprecated int save(int);
+    method public deprecated int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
     method public int saveLayer(android.graphics.RectF, android.graphics.Paint);
-    method public int saveLayer(float, float, float, float, android.graphics.Paint, int);
+    method public deprecated int saveLayer(float, float, float, float, android.graphics.Paint, int);
     method public int saveLayer(float, float, float, float, android.graphics.Paint);
-    method public int saveLayerAlpha(android.graphics.RectF, int, int);
+    method public deprecated int saveLayerAlpha(android.graphics.RectF, int, int);
     method public int saveLayerAlpha(android.graphics.RectF, int);
-    method public int saveLayerAlpha(float, float, float, float, int, int);
+    method public deprecated int saveLayerAlpha(float, float, float, float, int, int);
     method public int saveLayerAlpha(float, float, float, float, int);
     method public void scale(float, float);
     method public final void scale(float, float, float, float);
@@ -12988,11 +12998,11 @@
     method public void skew(float, float);
     method public void translate(float, float);
     field public static final int ALL_SAVE_FLAG = 31; // 0x1f
-    field public static final int CLIP_SAVE_FLAG = 2; // 0x2
-    field public static final int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
-    field public static final int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
-    field public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
-    field public static final int MATRIX_SAVE_FLAG = 1; // 0x1
+    field public static final deprecated int CLIP_SAVE_FLAG = 2; // 0x2
+    field public static final deprecated int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
+    field public static final deprecated int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
+    field public static final deprecated int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
+    field public static final deprecated int MATRIX_SAVE_FLAG = 1; // 0x1
   }
 
   public static final class Canvas.EdgeType extends java.lang.Enum {
@@ -14369,6 +14379,7 @@
     method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
     method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
     method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
+    method public static android.graphics.drawable.Icon createWithMaskableBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
     method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
     method public int describeContents();
@@ -14452,9 +14463,9 @@
   }
 
   public class MaskableIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
-    ctor public MaskableIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable getBackground();
+    method public static float getExtraInsetPercentage();
     method public android.graphics.drawable.Drawable getForeground();
     method public android.graphics.Path getIconMask();
     method public int getOpacity();
@@ -14464,8 +14475,6 @@
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void setOpacity(int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
-    field public static final float DEFAULT_VIEW_PORT_SCALE = 0.6666667f;
-    field public static final float MASK_SIZE = 100.0f;
   }
 
   public class NinePatchDrawable extends android.graphics.drawable.Drawable {
@@ -15027,6 +15036,7 @@
     field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
     field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light";
     field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration";
+    field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
     field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
@@ -15057,6 +15067,7 @@
     field public static final int TYPE_HEART_RATE = 21; // 0x15
     field public static final int TYPE_LIGHT = 5; // 0x5
     field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
+    field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final int TYPE_MOTION_DETECT = 30; // 0x1e
@@ -39271,12 +39282,37 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class FillCallback implements android.os.Parcelable {
+  public final class Dataset implements android.os.Parcelable {
     method public int describeContents();
-    method public void onFailure(java.lang.CharSequence);
-    method public void onSuccess(android.view.autofill.FillResponse);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.service.autofill.Dataset> CREATOR;
+  }
+
+  public static final class Dataset.Builder {
+    ctor public Dataset.Builder(java.lang.CharSequence);
+    method public android.service.autofill.Dataset build();
+    method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
+  }
+
+  public final class FillCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(android.service.autofill.FillResponse);
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR;
+  }
+
+  public static final class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
+    method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
+    method public android.service.autofill.FillResponse build();
+    method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
   public final class SaveCallback {
@@ -43464,6 +43500,7 @@
     method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.ChangedPackages getChangedPackages(int);
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -50294,6 +50331,8 @@
     method public void valueChanged(android.view.View);
     method public void virtualFocusChanged(android.view.View, int, android.graphics.Rect, boolean);
     method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
+    field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
+    field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
   }
 
   public final class AutoFillType implements android.os.Parcelable {
@@ -50321,35 +50360,6 @@
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR;
   }
 
-  public final class Dataset implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR;
-  }
-
-  public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
-    method public android.view.autofill.Dataset build();
-    method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
-    method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
-    method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
-  }
-
-  public final class FillResponse implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR;
-  }
-
-  public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder(java.lang.String);
-    method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
-    method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
-    method public android.view.autofill.FillResponse build();
-    method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
-    method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
-  }
-
 }
 
 package android.view.inputmethod {
@@ -50760,7 +50770,7 @@
 
   public final class TextClassificationManager {
     method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+    method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
   }
 
   public final class TextClassificationResult {
@@ -56255,6 +56265,13 @@
     field public static final java.lang.Class<java.lang.Boolean> TYPE;
   }
 
+  public class BootstrapMethodError extends java.lang.LinkageError {
+    ctor public BootstrapMethodError();
+    ctor public BootstrapMethodError(java.lang.String);
+    ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable);
+    ctor public BootstrapMethodError(java.lang.Throwable);
+  }
+
   public final class Byte extends java.lang.Number implements java.lang.Comparable {
     ctor public Byte(byte);
     ctor public Byte(java.lang.String) throws java.lang.NumberFormatException;
@@ -58141,6 +58158,21 @@
 
 package java.lang.invoke {
 
+  public abstract class CallSite {
+    method public abstract java.lang.invoke.MethodHandle dynamicInvoker();
+    method public abstract java.lang.invoke.MethodHandle getTarget();
+    method public abstract void setTarget(java.lang.invoke.MethodHandle);
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public class ConstantCallSite extends java.lang.invoke.CallSite {
+    ctor public ConstantCallSite(java.lang.invoke.MethodHandle);
+    ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable;
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public final void setTarget(java.lang.invoke.MethodHandle);
+  }
+
   public class LambdaConversionException extends java.lang.Exception {
     ctor public LambdaConversionException();
     ctor public LambdaConversionException(java.lang.String);
@@ -58269,6 +58301,22 @@
     method public java.lang.invoke.MethodType wrap();
   }
 
+  public class MutableCallSite extends java.lang.invoke.CallSite {
+    ctor public MutableCallSite(java.lang.invoke.MethodType);
+    ctor public MutableCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
+  public class VolatileCallSite extends java.lang.invoke.CallSite {
+    ctor public VolatileCallSite(java.lang.invoke.MethodType);
+    ctor public VolatileCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
   public class WrongMethodTypeException extends java.lang.RuntimeException {
     ctor public WrongMethodTypeException();
     ctor public WrongMethodTypeException(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9479927..fbd6b5e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -601,6 +601,8 @@
     field public static final int font = 16844082; // 0x1010532
     field public static final int fontFamily = 16843692; // 0x10103ac
     field public static final int fontFeatureSettings = 16843959; // 0x10104b7
+    field public static final int fontProviderAuthority = 16844114; // 0x1010552
+    field public static final int fontProviderQuery = 16844115; // 0x1010553
     field public static final int fontStyle = 16844081; // 0x1010531
     field public static final int fontWeight = 16844083; // 0x1010533
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -9208,10 +9210,6 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
-    field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
-    field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
-    field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
-    field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -9955,6 +9953,15 @@
     method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
   }
 
+  public final class ChangedPackages implements android.os.Parcelable {
+    ctor public ChangedPackages(int, java.util.List<java.lang.String>);
+    method public int describeContents();
+    method public java.util.List<java.lang.String> getPackageNames();
+    method public int getSequenceNumber();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR;
+  }
+
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
     ctor public ComponentInfo();
     ctor public ComponentInfo(android.content.pm.ComponentInfo);
@@ -10302,6 +10309,7 @@
     method public abstract java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.ChangedPackages getChangedPackages(int);
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -12415,14 +12423,14 @@
     method public void rotate(float);
     method public final void rotate(float, float, float);
     method public int save();
-    method public int save(int);
-    method public int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
+    method public deprecated int save(int);
+    method public deprecated int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
     method public int saveLayer(android.graphics.RectF, android.graphics.Paint);
-    method public int saveLayer(float, float, float, float, android.graphics.Paint, int);
+    method public deprecated int saveLayer(float, float, float, float, android.graphics.Paint, int);
     method public int saveLayer(float, float, float, float, android.graphics.Paint);
-    method public int saveLayerAlpha(android.graphics.RectF, int, int);
+    method public deprecated int saveLayerAlpha(android.graphics.RectF, int, int);
     method public int saveLayerAlpha(android.graphics.RectF, int);
-    method public int saveLayerAlpha(float, float, float, float, int, int);
+    method public deprecated int saveLayerAlpha(float, float, float, float, int, int);
     method public int saveLayerAlpha(float, float, float, float, int);
     method public void scale(float, float);
     method public final void scale(float, float, float, float);
@@ -12433,11 +12441,11 @@
     method public void skew(float, float);
     method public void translate(float, float);
     field public static final int ALL_SAVE_FLAG = 31; // 0x1f
-    field public static final int CLIP_SAVE_FLAG = 2; // 0x2
-    field public static final int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
-    field public static final int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
-    field public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
-    field public static final int MATRIX_SAVE_FLAG = 1; // 0x1
+    field public static final deprecated int CLIP_SAVE_FLAG = 2; // 0x2
+    field public static final deprecated int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
+    field public static final deprecated int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
+    field public static final deprecated int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
+    field public static final deprecated int MATRIX_SAVE_FLAG = 1; // 0x1
   }
 
   public static final class Canvas.EdgeType extends java.lang.Enum {
@@ -13814,6 +13822,7 @@
     method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
     method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
     method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
+    method public static android.graphics.drawable.Icon createWithMaskableBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
     method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
     method public int describeContents();
@@ -13897,20 +13906,19 @@
   }
 
   public class MaskableIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
-    ctor public MaskableIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable getBackground();
+    method public static float getExtraInsetPercentage();
     method public android.graphics.drawable.Drawable getForeground();
     method public android.graphics.Path getIconMask();
     method public int getOpacity();
+    method public android.graphics.Region getSafeZone();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
     method public void setAlpha(int);
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void setOpacity(int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
-    field public static final float DEFAULT_VIEW_PORT_SCALE = 0.6666667f;
-    field public static final float MASK_SIZE = 100.0f;
   }
 
   public class NinePatchDrawable extends android.graphics.drawable.Drawable {
@@ -14469,6 +14477,7 @@
     field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
     field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light";
     field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration";
+    field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
     field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
@@ -14497,6 +14506,7 @@
     field public static final int TYPE_HEART_RATE = 21; // 0x15
     field public static final int TYPE_LIGHT = 5; // 0x5
     field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
+    field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final int TYPE_MOTION_DETECT = 30; // 0x1e
@@ -36340,12 +36350,37 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class FillCallback implements android.os.Parcelable {
+  public final class Dataset implements android.os.Parcelable {
     method public int describeContents();
-    method public void onFailure(java.lang.CharSequence);
-    method public void onSuccess(android.view.autofill.FillResponse);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.service.autofill.Dataset> CREATOR;
+  }
+
+  public static final class Dataset.Builder {
+    ctor public Dataset.Builder(java.lang.CharSequence);
+    method public android.service.autofill.Dataset build();
+    method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
+  }
+
+  public final class FillCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(android.service.autofill.FillResponse);
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR;
+  }
+
+  public static final class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
+    method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
+    method public android.service.autofill.FillResponse build();
+    method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
   public final class SaveCallback {
@@ -40177,6 +40212,7 @@
     method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.ChangedPackages getChangedPackages(int);
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -47168,6 +47204,8 @@
     method public void valueChanged(android.view.View);
     method public void virtualFocusChanged(android.view.View, int, android.graphics.Rect, boolean);
     method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
+    field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
+    field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
   }
 
   public final class AutoFillType implements android.os.Parcelable {
@@ -47195,35 +47233,6 @@
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR;
   }
 
-  public final class Dataset implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR;
-  }
-
-  public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
-    method public android.view.autofill.Dataset build();
-    method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
-    method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
-    method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
-  }
-
-  public final class FillResponse implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR;
-  }
-
-  public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder(java.lang.String);
-    method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
-    method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
-    method public android.view.autofill.FillResponse build();
-    method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
-    method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
-  }
-
 }
 
 package android.view.inputmethod {
@@ -47634,7 +47643,7 @@
 
   public final class TextClassificationManager {
     method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+    method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
   }
 
   public final class TextClassificationResult {
@@ -52783,6 +52792,13 @@
     field public static final java.lang.Class<java.lang.Boolean> TYPE;
   }
 
+  public class BootstrapMethodError extends java.lang.LinkageError {
+    ctor public BootstrapMethodError();
+    ctor public BootstrapMethodError(java.lang.String);
+    ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable);
+    ctor public BootstrapMethodError(java.lang.Throwable);
+  }
+
   public final class Byte extends java.lang.Number implements java.lang.Comparable {
     ctor public Byte(byte);
     ctor public Byte(java.lang.String) throws java.lang.NumberFormatException;
@@ -54669,6 +54685,21 @@
 
 package java.lang.invoke {
 
+  public abstract class CallSite {
+    method public abstract java.lang.invoke.MethodHandle dynamicInvoker();
+    method public abstract java.lang.invoke.MethodHandle getTarget();
+    method public abstract void setTarget(java.lang.invoke.MethodHandle);
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public class ConstantCallSite extends java.lang.invoke.CallSite {
+    ctor public ConstantCallSite(java.lang.invoke.MethodHandle);
+    ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable;
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public final void setTarget(java.lang.invoke.MethodHandle);
+  }
+
   public class LambdaConversionException extends java.lang.Exception {
     ctor public LambdaConversionException();
     ctor public LambdaConversionException(java.lang.String);
@@ -54797,6 +54828,22 @@
     method public java.lang.invoke.MethodType wrap();
   }
 
+  public class MutableCallSite extends java.lang.invoke.CallSite {
+    ctor public MutableCallSite(java.lang.invoke.MethodType);
+    ctor public MutableCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
+  public class VolatileCallSite extends java.lang.invoke.CallSite {
+    ctor public VolatileCallSite(java.lang.invoke.MethodType);
+    ctor public VolatileCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
   public class WrongMethodTypeException extends java.lang.RuntimeException {
     ctor public WrongMethodTypeException();
     ctor public WrongMethodTypeException(java.lang.String);
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index a67e47f..bfcad1b 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -127,6 +127,11 @@
             return;
         }
 
+        if ("cancel".equals(op)) {
+            doCancel();
+            return;
+        }
+
         if ("whitelist".equals(op)) {
             doPrintWhitelist();
             return;
@@ -270,6 +275,8 @@
                 return "Agent error";
             case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
                 return "Size quota exceeded";
+            case BackupManager.ERROR_BACKUP_CANCELLED:
+                return "Backup Cancelled";
             default:
                 return "Unknown error";
         }
@@ -361,6 +368,21 @@
         }
     }
 
+    private void doCancel() {
+        String arg = nextArg();
+        if ("backups".equals(arg)) {
+            try {
+                mBmgr.cancelBackups();
+            } catch (RemoteException e) {
+                System.err.println(e.toString());
+                System.err.println(BMGR_NOT_RUNNING_ERR);
+            }
+            return;
+        }
+
+        System.err.println("Unknown command.");
+    }
+
     private void doTransport() {
         try {
             String which = nextArg();
@@ -721,6 +743,7 @@
         System.err.println("       bmgr wipe TRANSPORT PACKAGE");
         System.err.println("       bmgr fullbackup PACKAGE...");
         System.err.println("       bmgr backupnow --all|PACKAGE...");
+        System.err.println("       bmgr cancel backups");
         System.err.println("");
         System.err.println("The 'backup' command schedules a backup pass for the named package.");
         System.err.println("Note that the backup pass will effectively be a no-op if the package");
@@ -780,5 +803,6 @@
         System.err.println("For each package it will run key/value or full data backup ");
         System.err.println("depending on the package's manifest declarations.");
         System.err.println("The data is sent via the currently active transport.");
+        System.err.println("The 'cancel backups' command cancels all running backups.");
     }
 }
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 2940e86..fca26f8 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1865,7 +1865,8 @@
             if (mEvent == ANIMATION_START) {
                 return mNode.mStartTime;
             } else if (mEvent == ANIMATION_DELAY_ENDED) {
-                return mNode.mStartTime + mNode.mAnimation.getStartDelay();
+                return mNode.mStartTime == DURATION_INFINITE
+                        ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay();
             } else {
                 return mNode.mEndTime;
             }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index edf60b4..4449454 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,9 @@
 
 package android.app;
 
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillManager;
+import android.view.autofill.AutoFillValue;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ToolbarActionBar;
@@ -113,8 +116,6 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.autofill.AutoFillManager;
-import android.view.autofill.AutoFillSession;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
@@ -688,7 +689,8 @@
         implements LayoutInflater.Factory2,
         Window.Callback, KeyEvent.Callback,
         OnCreateContextMenuListener, ComponentCallbacks2,
-        Window.OnWindowDismissedCallback, WindowControllerCallback {
+        Window.OnWindowDismissedCallback, WindowControllerCallback,
+        AutoFillManager.AutoFillClient {
     private static final String TAG = "Activity";
     private static final boolean DEBUG_LIFECYCLE = false;
 
@@ -726,6 +728,7 @@
             "android:hasCurrentPermissionsRequest";
 
     private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
+    private static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:";
 
     private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui";
 
@@ -841,9 +844,6 @@
 
     private boolean mHasCurrentPermissionsRequest;
 
-    @GuardedBy("this")
-    private AutoFillSession mAutoFillSession;
-
     private static native String getDlWarning();
 
     /** Return the intent that started this activity. */
@@ -1695,25 +1695,6 @@
     }
 
     /**
-     * Lazily attachs the activity to the current {@link AutoFillSession} (if any).
-     */
-    void attachToAutoFillSession() {
-        synchronized (this) {
-            if (mAutoFillSession == null) {
-                final AutoFillManager afm = getSystemService(AutoFillManager.class);
-                if (afm != null) {
-                    mAutoFillSession = afm.getSession();
-                    if (mAutoFillSession != null) {
-                        mAutoFillSession.attachActivity(this);
-                    } else {
-                        Log.w(TAG, "attachToAutoFillSession(): not in a session");
-                    }
-                }
-            }
-        }
-    }
-
-    /**
      * Request the Keyboard Shortcuts screen to show up. This will trigger
      * {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
      */
@@ -1799,9 +1780,8 @@
         getApplication().dispatchActivityStopped(this);
         mTranslucentCallback = null;
         mCalled = true;
-        if (mAutoFillSession != null && isFinishing()) {
-            mAutoFillSession.finishSession();
-            mAutoFillSession = null;
+        if (isFinishing() && AutoFillManager.isClientActive(getActivityToken())) {
+            getSystemService(AutoFillManager.class).reset();
         }
     }
 
@@ -6021,11 +6001,6 @@
             getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
         }
 
-        if (mAutoFillSession!= null) {
-            writer.print(prefix); writer.print("mAutoFillSession: " );
-                    writer.println(mAutoFillSession);
-        }
-
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
     }
 
@@ -6748,6 +6723,8 @@
         mCurrentConfig = config;
 
         mWindow.setColorMode(info.colorMode);
+
+        AutoFillManager.addClient(token, this);
     }
 
     /** @hide */
@@ -7038,6 +7015,8 @@
                     return;
                 }
             }
+        } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
+            getSystemService(AutoFillManager.class).onAuthenticationResult(data);
         } else {
             Fragment frag = mFragments.findFragmentByWho(who);
             if (frag != null) {
@@ -7178,6 +7157,39 @@
         fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
     }
 
+    /** @hide */
+    @Override
+    public void autoFill(List<AutoFillId> ids, List<AutoFillValue> values) {
+        final View root = getWindow().getDecorView();
+        final int itemCount = ids.size();
+        for (int i = 0; i < itemCount; i++) {
+            final AutoFillId id = ids.get(i);
+            final AutoFillValue value = values.get(i);
+            final int viewId = id.getViewId();
+            final View view = root.findViewByAccessibilityIdTraversal(viewId);
+            if (view == null) {
+                Log.w(TAG, "autoFill(): no View with id " + viewId);
+                continue;
+            }
+            if (id.isVirtual()) {
+                view.autoFillVirtual(id.getVirtualChildId(), value);
+            } else {
+                view.autoFill(value);
+            }
+        }
+    }
+
+    /** @hide */
+    @Override
+    public void authenticate(IntentSender intent, Intent fillInIntent) {
+        try {
+            startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
+                    0, fillInIntent, 0, 0, null);
+        } catch (IntentSender.SendIntentException e) {
+            Log.e(TAG, "authenticate() failed for intent:" + intent, e);
+        }
+    }
+
     class HostCallbacks extends FragmentHostCallback<Activity> {
         public HostCallbacks() {
             super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index efe72c3..fda9966 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -133,7 +133,7 @@
     public final static boolean ENABLE_TASK_SNAPSHOTS;
 
     static {
-        ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", true);
+        ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", false);
     }
 
     static final class UidObserver extends IUidObserver.Stub {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index fa64a0f..4e34552 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -22,6 +22,7 @@
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.net.NetworkPolicyManager.UidStateWithSeqObserver;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.service.voice.IVoiceInteractionSession;
@@ -233,4 +234,16 @@
      * @see android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
      */
     public abstract void setHasOverlayUi(int pid, boolean hasOverlayUi);
+
+    /**
+     * Set observer which listens to uid state changes. Uid state change along with the sequence
+     * number associated with it needs to be passed to {@link UidStateWithSeqObserver}.
+     */
+    public abstract void setUidStateWithSeqObserver(UidStateWithSeqObserver observer);
+
+    /**
+     * Notifies that NetworkPolicyManagerService has updated the network policy rules for
+     * a specific {@param uid} and {@param procStateSeq}.
+     */
+    public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dffd81f..1f8e6db 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -89,8 +89,6 @@
 import android.provider.Settings;
 import android.security.NetworkSecurityPolicy;
 import android.security.net.config.NetworkSecurityConfigProvider;
-import android.service.autofill.AutoFillService;
-import android.service.autofill.IAutoFillAppCallback;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
@@ -180,6 +178,7 @@
     public static final boolean DEBUG_CONFIGURATION = false;
     private static final boolean DEBUG_SERVICE = false;
     private static final boolean DEBUG_MEMORY_TRIM = false;
+    private static final boolean DEBUG_NETWORK = false;
     private static final boolean DEBUG_PROVIDER = false;
     private static final boolean DEBUG_ORDER = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
@@ -202,6 +201,55 @@
     // Whether to invoke an activity callback after delivering new configuration.
     private static final boolean REPORT_TO_ACTIVITY = true;
 
+    /**
+     * This is the time main thread waits for the NetworkPolicyManagerService to notify
+     * that network is unrestricted. After this the app components will be launched anyway.
+     */
+    private long mWaitForNetworkTimeoutMs;
+
+    /**
+     * This is only for logging purposes. This will help us identify if the waiting for network
+     * is responsible for any lag that user might see.
+     */
+    private static final int WAIT_FOR_NETWORK_THRESHOLD_MS = 100; // 0.1 sec
+
+    /**
+     * State indicating that there is no need for any blocking for network.
+     */
+    public static final int NETWORK_STATE_NO_CHANGE = 0;
+
+    /**
+     * State indicating that main thread should wait for ActivityManagerService to notify
+     * before the app components are launched.
+     */
+    public static final int NETWORK_STATE_BLOCK = 1;
+
+    /**
+     * State indicating that any threads waiting for ActivityManagerService to notify should
+     * be unblocked.
+     */
+    public static final int NETWORK_STATE_UNBLOCK = 2;
+
+    /**
+     * Constant for indicating a invalid sequence number.
+     */
+    public static final long INVALID_PROC_STATE_SEQ = -1;
+
+    /**
+     * Current sequence number associated with the process state change.
+     */
+    @GuardedBy("mNetworkPolicyLock")
+    private long mCurProcStateSeq;
+
+    /**
+     * Indicates whether any component being launched should block for network before
+     * proceeding.
+     */
+    @GuardedBy("mNetworkPolicyLock")
+    private boolean mShouldBlockForNetwork;
+
+    private Object mNetworkPolicyLock = new Object();
+
     private ContextImpl mSystemContext;
 
     static volatile IPackageManager sPackageManager;
@@ -1306,6 +1354,18 @@
         }
 
         @Override
+        public void setBlockForNetworkState(int blockState, long targetProcStateSeq) {
+            synchronized (mNetworkPolicyLock) {
+                if (blockState == NETWORK_STATE_UNBLOCK) {
+                    unblockForNetworkAccessLN(targetProcStateSeq);
+                } else if (blockState == NETWORK_STATE_BLOCK) {
+                    mShouldBlockForNetwork = true;
+                }
+                mCurProcStateSeq = targetProcStateSeq;
+            }
+        }
+
+        @Override
         public void scheduleInstallProvider(ProviderInfo provider) {
             sendMessage(H.INSTALL_PROVIDER, provider);
         }
@@ -1388,6 +1448,13 @@
         public void handleTrustStorageUpdate() {
             NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate();
         }
+
+        @Override
+        public void notifyNetworkStateUpdated(long curProcStateSeq) {
+            synchronized (mNetworkPolicyLock) {
+                unblockForNetworkAccessLN(curProcStateSeq);
+            }
+        }
     }
 
     private int getLifecycleSeq() {
@@ -2079,6 +2146,79 @@
         }
     }
 
+    void blockForNetworkAccessInForegroundService(long procStateSeq) {
+        synchronized (mNetworkPolicyLock) {
+            if (mCurProcStateSeq >= procStateSeq) {
+                if (mShouldBlockForNetwork) {
+                    blockForNetworkAccessLN();
+                }
+            } else {
+                mCurProcStateSeq = procStateSeq;
+                mShouldBlockForNetwork = true;
+                blockForNetworkAccessLN();
+            }
+        }
+    }
+
+    /**
+     * Block for unrestricted network. It will register a listener to AMS and wait for it to
+     * notify that network policy rules are updated. This method is called before relevant app
+     * components are launched.
+     */
+    private void blockForNetworkAccessLN() {
+        try {
+            if (ActivityManager.getService().registerNetworkRulesUpdateListener(
+                    mAppThread, mCurProcStateSeq)) {
+                try {
+                    Slog.d(TAG, "Uid: " + mBoundApplication.appInfo.uid
+                            + " seq: " + mCurProcStateSeq
+                            + ". Blocking for network. callers: " + Debug.getCallers(3));
+                    final long blockStartTime = SystemClock.elapsedRealtime();
+                    mNetworkPolicyLock.wait(mWaitForNetworkTimeoutMs);
+                    final long totalWaitTime = (SystemClock.elapsedRealtime() - blockStartTime);
+                    if (totalWaitTime >= mWaitForNetworkTimeoutMs) {
+                        Slog.wtf(TAG, "Timed out waiting for the network rules to get updated."
+                                + " Uid: " + mBoundApplication.appInfo.uid + " seq: "
+                                + mCurProcStateSeq);
+                    } else if (totalWaitTime >= WAIT_FOR_NETWORK_THRESHOLD_MS) {
+                        Slog.d(TAG, "Waited for time greater than threshold."
+                                + " Uid: " + mBoundApplication.appInfo.uid + " seq: "
+                                + mCurProcStateSeq);
+                    }
+                    if (DEBUG_NETWORK) {
+                        Slog.d(TAG, "Uid: " + mBoundApplication.appInfo.uid
+                                + " seq: " + mCurProcStateSeq
+                                + ". Time waited for network: " + totalWaitTime);
+                    }
+                } catch (InterruptedException ignored) {
+                }
+            }
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    public void checkAndBlockForNetworkAccess() {
+        synchronized (mNetworkPolicyLock) {
+            if (mWaitForNetworkTimeoutMs > 0 && mShouldBlockForNetwork) {
+                blockForNetworkAccessLN();
+            }
+        }
+    }
+
+    /**
+     * Unblock the main thread if it is waiting for network.
+     */
+    private void unblockForNetworkAccessLN(long procStateSeq) {
+        if (mShouldBlockForNetwork && procStateSeq >= mCurProcStateSeq) {
+            if (DEBUG_NETWORK) {
+                Slog.d(TAG, "Unblocking threads waiting for network. uid: "
+                        + mBoundApplication.appInfo.uid + " procStateSeq: " + procStateSeq);
+            }
+            mNetworkPolicyLock.notifyAll();
+            mShouldBlockForNetwork = false;
+        }
+    }
+
     ActivityThread() {
         mResourcesManager = ResourcesManager.getInstance();
     }
@@ -2671,6 +2811,7 @@
                     activity.mIntent = customIntent;
                 }
                 r.lastNonConfigurationInstances = null;
+                checkAndBlockForNetworkAccess();
                 activity.mStartedActivity = false;
                 int theme = r.activityInfo.getThemeResource();
                 if (theme != 0) {
@@ -2931,16 +3072,13 @@
             if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutoFill) {
                 structure = new AssistStructure(r.activity, forAutoFill);
                 Intent activityIntent = r.activity.getIntent();
-                boolean attachToSession = false;
                 // TODO(b/33197203): re-evaluate conditions below for auto-fill. In particular,
                 // FLAG_SECURE might be allowed on AUTO_FILL but not on AUTO_FILL_SAVE)
                 boolean notSecure = r.window == null ||
                         (r.window.getAttributes().flags
                                 & WindowManager.LayoutParams.FLAG_SECURE) == 0;
                 if (activityIntent != null && notSecure) {
-                    if (forAutoFill) {
-                        attachToSession = true;
-                    } else {
+                    if (!forAutoFill) {
                         Intent intent = new Intent(activityIntent);
                         intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
@@ -2950,16 +3088,10 @@
                 } else {
                     if (!forAutoFill) {
                         content.setDefaultIntent(new Intent());
-                    } else {
-                        // activityIntent is unlikely to be null, but if it is, we should still
-                        // set the auto-fill callback.
-                        attachToSession = notSecure;
                     }
                 }
                 if (!forAutoFill) {
                     r.activity.onProvideAssistContent(content);
-                } else if (attachToSession) {
-                    r.activity.attachToAutoFillSession();
                 }
             }
         }
@@ -5350,6 +5482,9 @@
         View.mDebugViewAttributes =
                 mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0;
 
+        mWaitForNetworkTimeoutMs = mCoreSettings.getLong(
+                Settings.Global.WAIT_FOR_NETWORK_TIMEOUT_MS);
+
         /**
          * For system applications on userdebug/eng builds, log stack
          * traces of disk and network access to dropbox for analysis.
@@ -5587,6 +5722,24 @@
         } finally {
             StrictMode.setThreadPolicy(savedPolicy);
         }
+
+        // Preload fonts resources
+        try {
+            final ApplicationInfo info =
+                    getPackageManager().getApplicationInfo(
+                            data.appInfo.packageName,
+                            PackageManager.GET_META_DATA /*flags*/,
+                            UserHandle.myUserId());
+            if (info.metaData != null) {
+                final int preloadedFontsResource = info.metaData.getInt(
+                        ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
+                if (preloadedFontsResource != 0) {
+                    data.info.mResources.preloadFonts(preloadedFontsResource);
+                }
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0c6c4ba..333e412 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -29,6 +29,7 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ChangedPackages;
 import android.content.pm.ComponentInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
@@ -506,6 +507,15 @@
     }
 
     @Override
+    public ChangedPackages getChangedPackages(int sequenceNumber) {
+        try {
+            return mPM.getChangedPackages(sequenceNumber, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public FeatureInfo[] getSystemAvailableFeatures() {
         try {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 585bd05..6717491 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -199,7 +199,7 @@
     int getRequestedOrientation(in IBinder token);
     void unbindFinished(in IBinder token, in Intent service, boolean doRebind);
     void setProcessForeground(in IBinder token, int pid, boolean isForeground);
-    void setServiceForeground(in ComponentName className, in IBinder token,
+    long setServiceForeground(in ComponentName className, in IBinder token,
             int id, in Notification notification, int flags);
     boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
     void getMemoryInfo(out ActivityManager.MemoryInfo outInfo);
@@ -210,6 +210,7 @@
     boolean killPids(in int[] pids, in String reason, boolean secure);
     List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags);
     ActivityManager.TaskThumbnail getTaskThumbnail(int taskId);
+    ActivityManager.TaskDescription getTaskDescription(int taskId);
     // Retrieve running application processes in the system
     List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
     // Get device configuration
@@ -603,6 +604,16 @@
     ActivityManager.TaskSnapshot getTaskSnapshot(int taskId);
 
     void scheduleApplicationInfoChanged(in List<String> packageNames, int userId);
+     /**
+      * Registers a listener for network rules state. When the network policy rules in
+      * NetworkPolicyManagerService are updated, ActivityManagerService will notify these
+      * registered listeners.
+      *
+      * @param procStateSeq The sequence number for which the listener is interested in knowing
+      *                     the network policy rules state.
+      * @return true if the listener is registered, false otherwise.
+      */
+    boolean registerNetworkRulesUpdateListener(IApplicationThread listener, long procStateSeq);
 
     // WARNING: when these transactions are updated, check if they are any callers on the native
     // side. If so, make sure they are using the correct transaction ids and arguments.
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 4fc6fb9..7378e2b 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -153,4 +153,6 @@
     void handleTrustStorageUpdate();
     void attachAgent(String path);
     void scheduleApplicationInfoChanged(in ApplicationInfo ai);
+    void setBlockForNetworkState(int blockState, long procStateSeq);
+    void notifyNetworkStateUpdated(long procStateSeq);
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 7900fc5..45a46b3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4082,16 +4082,23 @@
 
         /**
          * Construct a RemoteViews for the final heads-up notification layout.
+         *
+         * @param increasedHeight true if this layout be created with an increased height. Some
+         * styles may support showing more then just that basic 1U size
+         * and the system may decide to render important notifications
+         * slightly bigger even when collapsed.
+         *
+         * @hide
          */
-        public RemoteViews createHeadsUpContentView() {
+        public RemoteViews createHeadsUpContentView(boolean increasedHeight) {
             if (mN.headsUpContentView != null
                     && (mStyle == null ||  !mStyle.displayCustomViewInline())) {
                 return mN.headsUpContentView;
             } else if (mStyle != null) {
-                    final RemoteViews styleView = mStyle.makeHeadsUpContentView();
-                    if (styleView != null) {
-                        return styleView;
-                    }
+                final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight);
+                if (styleView != null) {
+                    return styleView;
+                }
             } else if (mActions.size() == 0) {
                 return null;
             }
@@ -4100,6 +4107,13 @@
         }
 
         /**
+         * Construct a RemoteViews for the final heads-up notification layout.
+         */
+        public RemoteViews createHeadsUpContentView() {
+            return createHeadsUpContentView(false /* useIncreasedHeight */);
+        }
+
+        /**
          * Construct a RemoteViews for the display in public contexts like on the lockscreen.
          *
          * @hide
@@ -4823,9 +4837,11 @@
 
         /**
          * Construct a Style-specific RemoteViews for the final HUN layout.
+         *
+         * @param increasedHeight true if this layout be created with an increased height.
          * @hide
          */
-        public RemoteViews makeHeadsUpContentView() {
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
             return null;
         }
 
@@ -5171,6 +5187,17 @@
         /**
          * @hide
          */
+        @Override
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+            if (increasedHeight && mBuilder.mActions.size() > 0) {
+                return makeBigContentView();
+            }
+            return super.makeHeadsUpContentView(increasedHeight);
+        }
+
+        /**
+         * @hide
+         */
         public RemoteViews makeBigContentView() {
 
             // Nasty
@@ -5578,7 +5605,10 @@
          * @hide
          */
         @Override
-        public RemoteViews makeHeadsUpContentView() {
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+            if (increasedHeight) {
+                return makeBigContentView();
+            }
             Message m = findLatestIncomingMessage();
             CharSequence title = mConversationTitle != null
                     ? mConversationTitle
@@ -6028,7 +6058,7 @@
          * @hide
          */
         @Override
-        public RemoteViews makeHeadsUpContentView() {
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
             RemoteViews expanded = makeMediaBigContentView();
             return expanded != null ? expanded : makeMediaContentView();
         }
@@ -6208,7 +6238,7 @@
          * @hide
          */
         @Override
-        public RemoteViews makeHeadsUpContentView() {
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
             return makeDecoratedHeadsUpContentView();
         }
 
@@ -6344,7 +6374,7 @@
          * @hide
          */
         @Override
-        public RemoteViews makeHeadsUpContentView() {
+        public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
             RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
                     ? mBuilder.mN.headsUpContentView
                     : mBuilder.mN.contentView;
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index 0ae8505..6ee4780 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -16,197 +16,86 @@
 
 package android.app;
 
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.LinkedList;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 /**
- * Internal utility class to keep track of process-global work that's outstanding and hasn't been
- * finished yet.
+ * Internal utility class to keep track of process-global work that's
+ * outstanding and hasn't been finished yet.
  *
- * New work will be {@link #queue queued}.
- *
- * It is possible to add 'finisher'-runnables that are {@link #waitToFinish guaranteed to be run}.
- * This is used to make sure the work has been finished.
- *
- * This was created for writing SharedPreference edits out asynchronously so we'd have a mechanism
- * to wait for the writes in Activity.onPause and similar places, but we may use this mechanism for
- * other things in the future.
- *
- * The queued asynchronous work is performed on a separate, dedicated thread.
+ * This was created for writing SharedPreference edits out
+ * asynchronously so we'd have a mechanism to wait for the writes in
+ * Activity.onPause and similar places, but we may use this mechanism
+ * for other things in the future.
  *
  * @hide
  */
 public class QueuedWork {
-    private static final String LOG_TAG = QueuedWork.class.getSimpleName();
 
-    /** Delay for delayed runnables */
-    private static final long DELAY = 50;
+    // The set of Runnables that will finish or wait on any async
+    // activities started by the application.
+    private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
+            new ConcurrentLinkedQueue<Runnable>();
 
-    /** Lock for this class */
-    private static final Object sLock = new Object();
-
-    /** Finishers {@link #addFinisher added} and not yet {@link #removeFinisher removed} */
-    @GuardedBy("sLock")
-    private static final LinkedList<Runnable> sFinishers = new LinkedList<>();
-
-    /** {@link #getHandler() Lazily} created handler */
-    @GuardedBy("sLock")
-    private static Handler sHandler = null;
-
-    /** Work queued via {@link #queue} */
-    @GuardedBy("sLock")
-    private static final LinkedList<Runnable> sWork = new LinkedList<>();
-
-    /** If new work can be delayed or not */
-    @GuardedBy("sLock")
-    private static boolean sCanDelay = true;
+    private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class
 
     /**
-     * Lazily create a handler on a separate thread.
-     *
-     * @return the handler
+     * Returns a single-thread Executor shared by the entire process,
+     * creating it if necessary.
      */
-    private static Handler getHandler() {
-        synchronized (sLock) {
-            if (sHandler == null) {
-                HandlerThread handlerThread = new HandlerThread("queued-work-looper",
-                        Process.THREAD_PRIORITY_BACKGROUND);
-                handlerThread.start();
-
-                sHandler = new QueuedWorkHandler(handlerThread.getLooper());
+    public static ExecutorService singleThreadExecutor() {
+        synchronized (QueuedWork.class) {
+            if (sSingleThreadExecutor == null) {
+                // TODO: can we give this single thread a thread name?
+                sSingleThreadExecutor = Executors.newSingleThreadExecutor();
             }
-            return sHandler;
+            return sSingleThreadExecutor;
         }
     }
 
     /**
-     * Add a finisher-runnable to wait for {@link #queue asynchronously processed work}.
+     * Add a runnable to finish (or wait for) a deferred operation
+     * started in this context earlier.  Typically finished by e.g.
+     * an Activity#onPause.  Used by SharedPreferences$Editor#startCommit().
      *
-     * Used by SharedPreferences$Editor#startCommit().
-     *
-     * Note that this doesn't actually start it running.  This is just a scratch set for callers
-     * doing async work to keep updated with what's in-flight. In the common case, caller code
-     * (e.g. SharedPreferences) will pretty quickly call remove() after an add(). The only time
-     * these Runnables are run is from {@link #waitToFinish}.
-     *
-     * @param finisher The runnable to add as finisher
+     * Note that this doesn't actually start it running.  This is just
+     * a scratch set for callers doing async work to keep updated with
+     * what's in-flight.  In the common case, caller code
+     * (e.g. SharedPreferences) will pretty quickly call remove()
+     * after an add().  The only time these Runnables are run is from
+     * waitToFinish(), below.
      */
-    public static void addFinisher(Runnable finisher) {
-        synchronized (sLock) {
-            sFinishers.add(finisher);
-        }
+    public static void add(Runnable finisher) {
+        sPendingWorkFinishers.add(finisher);
+    }
+
+    public static void remove(Runnable finisher) {
+        sPendingWorkFinishers.remove(finisher);
     }
 
     /**
-     * Remove a previously {@link #addFinisher added} finisher-runnable.
+     * Finishes or waits for async operations to complete.
+     * (e.g. SharedPreferences$Editor#startCommit writes)
      *
-     * @param finisher The runnable to remove.
-     */
-    public static void removeFinisher(Runnable finisher) {
-        synchronized (sLock) {
-            sFinishers.remove(finisher);
-        }
-    }
-
-    /**
-     * Trigger queued work to be processed immediately. The queued work is processed on a separate
-     * thread asynchronous. While doing that run and process all finishers on this thread. The
-     * finishers can be implemented in a way to check weather the queued work is finished.
-     *
-     * Is called from the Activity base class's onPause(), after BroadcastReceiver's onReceive,
-     * after Service command handling, etc. (so async work is never lost)
+     * Is called from the Activity base class's onPause(), after
+     * BroadcastReceiver's onReceive, after Service command handling,
+     * etc.  (so async work is never lost)
      */
     public static void waitToFinish() {
-        Handler handler = getHandler();
-
-        synchronized (sLock) {
-            if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) {
-                // Force the delayed work to be processed now
-                handler.removeMessages(QueuedWorkHandler.MSG_RUN);
-                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
-            }
-
-            // We should not delay any work as this might delay the finishers
-            sCanDelay = false;
-        }
-
-        try {
-            while (true) {
-                Runnable finisher;
-
-                synchronized (sLock) {
-                    finisher = sFinishers.poll();
-                }
-
-                if (finisher == null) {
-                    break;
-                }
-
-                finisher.run();
-            }
-        } finally {
-            sCanDelay = true;
+        Runnable toFinish;
+        while ((toFinish = sPendingWorkFinishers.poll()) != null) {
+            toFinish.run();
         }
     }
-
+    
     /**
-     * Queue a work-runnable for processing asynchronously.
-     *
-     * @param work The new runnable to process
-     * @param shouldDelay If the message should be delayed
-     */
-    public static void queue(Runnable work, boolean shouldDelay) {
-        Handler handler = getHandler();
-
-        synchronized (sLock) {
-            sWork.add(work);
-
-            if (shouldDelay && sCanDelay) {
-                handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
-            } else {
-                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
-            }
-        }
-    }
-
-    /**
-     * @return True iff there is any {@link #queue async work queued}.
+     * Returns true if there is pending work to be done.  Note that the
+     * result is out of data as soon as you receive it, so be careful how you
+     * use it.
      */
     public static boolean hasPendingWork() {
-        synchronized (sLock) {
-            return !sWork.isEmpty();
-        }
+        return !sPendingWorkFinishers.isEmpty();
     }
-
-    private static class QueuedWorkHandler extends Handler {
-        static final int MSG_RUN = 1;
-
-        QueuedWorkHandler(Looper looper) {
-            super(looper);
-        }
-
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_RUN) {
-                LinkedList<Runnable> work;
-
-                synchronized (sWork) {
-                    work = (LinkedList<Runnable>) sWork.clone();
-                    sWork.clear();
-
-                    // Remove all msg-s as all work will be processed now
-                    removeMessages(MSG_RUN);
-                }
-
-                work.forEach(Runnable::run);
-            }
-        }
-    }
+    
 }
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 4fe4f98..9cd048e 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -683,26 +683,28 @@
      * flag if killing your service would be disruptive to the user, such as
      * if your service is performing background music playback, so the user
      * would notice if their music stopped playing.
-     * 
+     *
      * <p>If you need your application to run on platform versions prior to API
      * level 5, you can use the following model to call the the older setForeground()
      * or this modern method as appropriate:
-     * 
+     *
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
      *   foreground_compatibility}
-     * 
+     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
      * @param notification The Notification to be displayed.
-     * 
+     *
      * @see #stopForeground(boolean)
      */
     public final void startForeground(int id, Notification notification) {
         try {
-            mActivityManager.setServiceForeground(
-                    new ComponentName(this, mClassName), mToken, id,
-                    notification, 0);
+            final long procStateSeq = mActivityManager.setServiceForeground(
+                    new ComponentName(this, mClassName), mToken, id, notification, 0);
+            if (procStateSeq != ActivityThread.INVALID_PROC_STATE_SEQ && mThread != null) {
+                mThread.blockForNetworkAccessInForegroundService(procStateSeq);
+            }
         } catch (RemoteException ex) {
         }
     }
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 023b4f3..3bb7019 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -406,12 +406,12 @@
                     }
                 };
 
-            QueuedWork.addFinisher(awaitCommit);
+            QueuedWork.add(awaitCommit);
 
             Runnable postWriteRunnable = new Runnable() {
                     public void run() {
                         awaitCommit.run();
-                        QueuedWork.removeFinisher(awaitCommit);
+                        QueuedWork.remove(awaitCommit);
                     }
                 };
 
@@ -588,10 +588,10 @@
         }
 
         if (DEBUG) {
-            Log.d(TAG, "queued " + mcr.memoryStateGeneration + " -> " + mFile.getName());
+            Log.d(TAG, "added " + mcr.memoryStateGeneration + " -> " + mFile.getName());
         }
 
-        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
+        QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
     }
 
     private static FileOutputStream createFileOutputStream(File file) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 44db326..f330a4b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -114,7 +114,8 @@
 import android.os.storage.StorageManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
-import android.service.autofill.IAutoFillManagerService;
+import android.view.autofill.AutoFillManager;
+import android.view.autofill.IAutoFillManager;
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.service.vr.IVrManager;
@@ -130,7 +131,6 @@
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
-import android.view.autofill.AutoFillManager;
 import android.view.inputmethod.InputMethodManager;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textservice.TextServicesManager;
@@ -826,8 +826,8 @@
             @Override
             public AutoFillManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getServiceOrThrow(Context.AUTO_FILL_MANAGER_SERVICE);
-                IAutoFillManagerService service = IAutoFillManagerService.Stub.asInterface(b);
-                return new AutoFillManager(ctx, service);
+                IAutoFillManager service = IAutoFillManager.Stub.asInterface(b);
+                return new AutoFillManager(ctx.getOuterContext(), service);
             }});
 
         registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 59bd01f..9d02f53 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -89,6 +89,14 @@
     public static final int ERROR_PACKAGE_NOT_FOUND = -2002;
 
     /**
+     * The backup operation was cancelled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_BACKUP_CANCELLED = -2003;
+
+    /**
      * The transport for some reason was not in a good state and
      * aborted the entire backup request. This is a transient
      * failure and should not be retried immediately.
@@ -626,6 +634,26 @@
         return -1;
     }
 
+    /**
+     * Cancel all running backups. After this call returns, no currently running backups will
+     * interact with the selected transport.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void cancelBackups() {
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                sService.cancelBackups();
+            } catch (RemoteException e) {
+                Log.e(TAG, "cancelBackups() couldn't connect.");
+            }
+        }
+    }
+
     /*
      * We wrap incoming binder calls with a private class implementation that
      * redirects them into main-thread actions.  This serializes the backup
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 393667d..59a941a 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -386,4 +386,10 @@
      */
     int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
         int flags);
+
+    /**
+     * Cancel all running backups. After this call returns, no currently running backups will
+     * interact with the selected transport.
+     */
+    void cancelBackups();
 }
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index c3d6606..485d078 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -211,16 +211,16 @@
                     // of the list to finish the broadcast, so we don't block this
                     // thread (which may be the main thread) to have it finished.
                     //
-                    // Note that we don't need to use QueuedWork.addFinisher() with the
+                    // Note that we don't need to use QueuedWork.add() with the
                     // runnable, since we know the AM is waiting for us until the
                     // executor gets to it.
-                    QueuedWork.queue(new Runnable() {
+                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
                         @Override public void run() {
                             if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                     "Finishing broadcast after work to component " + mToken);
                             sendFinished(mgr);
                         }
-                    }, false);
+                    });
                 } else {
                     if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                             "Finishing broadcast to component " + mToken);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 4480b41..5bbbcc2 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1886,7 +1886,7 @@
                 ContentProvider.getUriWithoutUserId(uri),
                 notifyForDescendants,
                 observer,
-                ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));
+                ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
     }
 
     /** @hide - designated user version */
@@ -1956,7 +1956,7 @@
                 ContentProvider.getUriWithoutUserId(uri),
                 observer,
                 syncToNetwork,
-                ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));
+                ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
     }
 
     /**
@@ -1982,7 +1982,7 @@
                 ContentProvider.getUriWithoutUserId(uri),
                 observer,
                 flags,
-                ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));
+                ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
     }
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5f4c36c..028a7bcf 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1806,41 +1806,6 @@
     @SystemApi
     public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
 
-    /**
-     * Intent extra: An id if an autofill item ({@link
-     * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}).
-     * <p>
-     * Type: String
-     * </p>
-     */
-    public static final String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
-
-    /**
-     * Intent extra: The assist structure which captures the filled screen.
-     * <p>
-     * Type: {@link android.app.assist.AssistStructure}
-     * </p>
-     */
-    public static final String EXTRA_AUTO_FILL_ASSIST_STRUCTURE =
-            "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
-
-    /**
-     * Intent extra: The metadata associated with the authenticated entity ({@link
-     * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}).
-     * <p>
-     * Type: {@link android.os.Bundle}
-     * </p>
-     */
-    public static final String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
-
-    /**
-     * Intent extra: A callback to report an authentication result.
-     * <p>
-     * Type: {@link android.view.autofill.FillResponse}
-     * </p>
-     */
-    public static final String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
-
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8465f0f..1fa4181 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -573,6 +573,11 @@
     public int privateFlags;
 
     /**
+     * @hide
+     */
+    public static final String METADATA_PRELOADED_FONTS = "preloaded_fonts";
+
+    /**
      * The required smallest screen width the application can run on.  If 0,
      * nothing has been specified.  Comes from
      * {@link android.R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
diff --git a/core/java/android/view/autofill/FillResponse.aidl b/core/java/android/content/pm/ChangedPackages.aidl
similarity index 83%
copy from core/java/android/view/autofill/FillResponse.aidl
copy to core/java/android/content/pm/ChangedPackages.aidl
index b018f15..1a9f5a1 100644
--- a/core/java/android/view/autofill/FillResponse.aidl
+++ b/core/java/android/content/pm/ChangedPackages.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2016, The Android Open Source Project
+ * Copyright (c) 2017, 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.view.autofill;
+package android.content.pm;
 
-parcelable FillResponse;
\ No newline at end of file
+parcelable ChangedPackages;
\ No newline at end of file
diff --git a/core/java/android/content/pm/ChangedPackages.java b/core/java/android/content/pm/ChangedPackages.java
new file mode 100644
index 0000000..94b8a5d
--- /dev/null
+++ b/core/java/android/content/pm/ChangedPackages.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Packages that have been changed since the last time they
+ * were requested.
+ */
+public final class ChangedPackages implements Parcelable {
+    /** The last known sequence number for these changes */
+    private final int mSequenceNumber;
+    /** The names of the packages that have changed */
+    private final List<String> mPackageNames;
+
+    public ChangedPackages(int sequenceNumber, @NonNull List<String> packageNames) {
+        this.mSequenceNumber = sequenceNumber;
+        this.mPackageNames = packageNames;
+    }
+
+    /** @hide */
+    protected ChangedPackages(Parcel in) {
+        mSequenceNumber = in.readInt();
+        mPackageNames = in.createStringArrayList();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSequenceNumber);
+        dest.writeStringList(mPackageNames);
+    }
+
+    /**
+     * Returns the last known sequence number for these changes.
+     */
+    public int getSequenceNumber() {
+        return mSequenceNumber;
+    }
+
+    /**
+     * Returns the names of the packages that have changed.
+     */
+    public @NonNull List<String> getPackageNames() {
+        return mPackageNames;
+    }
+
+    public static final Parcelable.Creator<ChangedPackages> CREATOR =
+            new Parcelable.Creator<ChangedPackages>() {
+        public ChangedPackages createFromParcel(Parcel in) {
+            return new ChangedPackages(in);
+        }
+
+        public ChangedPackages[] newArray(int size) {
+            return new ChangedPackages[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3fb46cf..9d36a73 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -23,6 +23,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ContainerEncryptionParams;
+import android.content.pm.ChangedPackages;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageInstallObserver2;
@@ -611,6 +612,8 @@
     String getServicesSystemSharedLibraryPackageName();
     String getSharedSystemSharedLibraryPackageName();
 
+    ChangedPackages getChangedPackages(int sequenceNumber, int userId);
+
     boolean isPackageDeviceAdminOnAnyUser(String packageName);
 
     List<String> getPreviousCodePaths(in String packageName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b20b5e2..308153d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,6 +20,7 @@
 import android.annotation.CheckResult;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -3853,6 +3854,17 @@
     public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
 
     /**
+     * Returns the names of the packages that have been changed
+     * [eg. added, removed or updated] since the given sequence
+     * number.
+     * <p>If no packages have been changed, returns <code>null</code>.
+     * <p>The sequence number starts at <code>0</code> and is
+     * reset every boot.
+     */
+    public abstract @Nullable ChangedPackages getChangedPackages(
+            @IntRange(from=0) int sequenceNumber);
+
+    /**
      * Get a list of features that are available on the
      * system.
      *
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 7ea62db..3f8f90e 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -64,6 +64,17 @@
 
     private static FontConfig.Family readFamily(XmlPullParser parser, Resources resources)
             throws XmlPullParserException, IOException {
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
+        String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
+        String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
+        array.recycle();
+        if (authority != null && query != null) {
+            while (parser.next() != XmlPullParser.END_TAG) {
+                skip(parser);
+            }
+            return new FontConfig.Family(authority, query);
+        }
         List<FontConfig.Font> fonts = new ArrayList<>();
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
@@ -84,11 +95,12 @@
         int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight, NORMAL_WEIGHT);
         boolean isItalic = ITALIC == array.getInt(R.styleable.FontFamilyFont_fontStyle, 0);
         String filename = array.getString(R.styleable.FontFamilyFont_font);
+        int resourceId = array.getResourceId(R.styleable.FontFamilyFont_font, 0);
         array.recycle();
         while (parser.next() != XmlPullParser.END_TAG) {
             skip(parser);
         }
-        return new FontConfig.Font(filename, 0, null, weight, isItalic);
+        return new FontConfig.Font(filename, 0, null, weight, isItalic, resourceId);
     }
 
     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 04e4454..21d4b22 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -373,6 +373,20 @@
     }
 
     /**
+     * @hide
+     */
+    public void preloadFonts(@FontRes int id) {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            impl.preloadFonts(this, value, id);
+        } finally {
+            releaseTempTypedValue(value);
+        }
+    }
+
+    /**
      * Returns the character sequence necessary for grammatically correct pluralization
      * of the given resource ID for the given quantity.
      * Note that the character sequence is selected based solely on grammatical necessity,
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index bf81096..38efa49 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -32,6 +32,7 @@
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.Configuration.NativeConfig;
 import android.content.res.Resources.NotFoundException;
+import android.graphics.FontFamily;
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -52,6 +53,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -782,6 +784,43 @@
     }
 
     /**
+     * @hide
+     */
+    public void preloadFonts(Resources wrapper, TypedValue value, int id) {
+        if (value.string == null) {
+            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+                    + Integer.toHexString(id) + ") is not a Font: " + value);
+        }
+
+        final String file = value.string.toString();
+
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+        try {
+            final XmlResourceParser rp = loadXmlResourceParser(
+                    file, id, value.assetCookie, "font");
+            final FontConfig config = FontResourcesParser.parse(rp, wrapper);
+            final List<FontConfig.Family> families = config.getFamilies();
+            if (families == null || families.isEmpty()) {
+                return;
+            }
+            for (int j = 0; j < families.size(); j++) {
+                final FontConfig.Family family = families.get(j);
+                final List<FontConfig.Font> fonts = family.getFonts();
+                for (int i = 0; i < fonts.size(); i++) {
+                    int resourceId = fonts.get(i).getResourceId();
+                    wrapper.getFont(resourceId);
+                }
+            }
+        } catch (XmlPullParserException e) {
+            Log.e(TAG, "Failed to parse xml resource " + file, e);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to read xml resource " + file, e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+    }
+
+    /**
      * Given the value and id, we can get the XML filename as in value.data, based on that, we
      * first try to load CSL from the cache. If not found, try to get from the constant state.
      * Last, parse the XML and generate the CSL.
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index d87c55e..8c13cc8 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -649,9 +649,22 @@
      * to be in the same order as the HAL. Skipping this sensor
      */
 
-    /* TYPE_LOW_LATENCY_OFF_BODY_SENSOR - defined as type 34 in the HAL needs to
-     * be defined in this space.
+    /**
+     * A constant describing a low latency off-body detect sensor.
+     *
+     * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+     *
      */
+    public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34;
+
+
+    /**
+     * A constant string describing a low-latency offbody detector sensor.
+     *
+     * @see #TYPE_LOW_LATENCY_OFFBODY_DETECT
+     */
+    public static final String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT =
+            "android.sensor.low_latency_offbody";
 
     /**
      * A constant describing an uncalibrated accelerometer sensor.
@@ -669,6 +682,7 @@
      */
     public static final String STRING_TYPE_ACCELEROMETER_UNCALIBRATED =
             "android.sensor.accelerometer_uncalibrated";
+
     /**
      * A constant describing all sensor types.
      */
@@ -786,7 +800,7 @@
             1, // SENSOR_TYPE_HEART_BEAT
             2, // SENSOR_TYPE_DYNAMIC_SENSOR_META
             16,// skip over additional sensor info type
-            1, // reserving for LLOB sensor type
+            1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT
             6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
     };
 
@@ -1195,6 +1209,9 @@
             case TYPE_DYNAMIC_SENSOR_META:
                 mStringType = STRING_TYPE_DYNAMIC_SENSOR_META;
                 return true;
+            case TYPE_LOW_LATENCY_OFFBODY_DETECT:
+                mStringType = STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT;
+                return true;
             case TYPE_ACCELEROMETER_UNCALIBRATED:
                 mStringType = STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
                 return true;
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9b72757a..c0bca97 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -564,6 +564,42 @@
      * completely unlikely to be anywhere else on the QRS complex.
      * </p>
      *
+     * <h4>{@link android.hardware.Sensor#TYPE_LOW_LATENCY_OFFBODY_DETECT
+     * Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT}:</h4>
+     *
+     * <p>
+     * A sensor of this type returns an event every time the device transitions
+     * from off-body to on-body and from on-body to off-body (e.g. a wearable
+     * device being removed from the wrist would trigger an event indicating an
+     * off-body transition). The event returned will contain a single value to
+     * indicate off-body state:
+     * </p>
+     *
+     * <ul>
+     *  <li> values[0]: off-body state</li>
+     * </ul>
+     *
+     * <p>
+     *     Valid values for off-body state:
+     * <ul>
+     *  <li> 1.0 (device is on-body)</li>
+     *  <li> 0.0 (device is off-body)</li>
+     * </ul>
+     * </p>
+     *
+     * <p>
+     * When a sensor of this type is activated, it must deliver the initial
+     * on-body or off-body event representing the current device state within
+     * 5 seconds of activating the sensor.
+     * </p>
+     *
+     * <p>
+     * This sensor must be able to detect and report an on-body to off-body
+     * transition within 1 second of the device being removed from the body,
+     * and must be able to detect and report an off-body to on-body transition
+     * within 5 seconds of the device being put back onto the body.
+     * </p>
+     *
      * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_UNCALIBRATED
      * Sensor.TYPE_ACCELEROMETER_UNCALIBRATED}:</h4> All values are in SI
      * units (m/s^2)
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index bcebb7d..dcd069d 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -250,6 +250,10 @@
      * application must then call {@link OutputConfiguration#addSurface} before finalizing the
      * configuration with this method.</p>
      *
+     * <p>If the provided OutputConfigurations are unchanged from session creation, this function
+     * call has no effect. This function must only be called once for a particular output
+     * configuration. </p>
+     *
      * <p>The output Surfaces included by this list of
      * {@link OutputConfiguration OutputConfigurations} can be used as {@link CaptureRequest}
      * targets as soon as this call returns.</p>
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index dc5750d..acb1d07 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1453,8 +1453,9 @@
     private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) {
         if (delay >= 0) {
             Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay);
-            Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
-            sCallbackHandler.sendMessageDelayed(msg, delay);
+            CallbackHandler handler = getHandler();
+            Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+            handler.sendMessageDelayed(msg, delay);
         }
     }
 
@@ -2897,19 +2898,19 @@
         }
     }
 
-    static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
-    static CallbackHandler sCallbackHandler;
+    private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
+    private static CallbackHandler sCallbackHandler;
 
-    private final static int LISTEN  = 1;
-    private final static int REQUEST = 2;
+    private static final int LISTEN  = 1;
+    private static final int REQUEST = 2;
 
     private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
             NetworkCallback callback, int timeoutMs, int action, int legacyType) {
-        return sendRequestForNetwork(need, callback, getHandler(), timeoutMs, action, legacyType);
+        return sendRequestForNetwork(need, callback, timeoutMs, action, legacyType, getHandler());
     }
 
-    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
-            NetworkCallback callback, Handler handler, int timeoutMs, int action, int legacyType) {
+    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
+            int timeoutMs, int action, int legacyType, CallbackHandler handler) {
         if (callback == null) {
             throw new IllegalArgumentException("null NetworkCallback");
         }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 1b715af..4b184f1 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.GET_SIGNATURES;
 import static android.net.NetworkPolicy.CYCLE_NONE;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -65,6 +66,8 @@
      *
      * See network-policy-restrictions.md for more info.
      */
+    /** Not a valid rule */
+    public static final int RULE_INVALID = -1;
     /** No specific rule was set */
     public static final int RULE_NONE = 0;
     /** Allow traffic on metered networks. */
@@ -360,6 +363,8 @@
         final StringBuilder string = new StringBuilder().append(uidRules).append(" (");
         if (uidRules == RULE_NONE) {
             string.append("NONE");
+        } else if (uidRules == RULE_INVALID) {
+            string.append("INVALID");
         } else {
             string.append(DebugUtils.flagsToString(NetworkPolicyManager.class, "RULE_", uidRules));
         }
@@ -381,4 +386,25 @@
         string.append(")");
         return string.toString();
     }
+
+    /**
+     * @hide
+     */
+    public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
+        return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isProcStateAllowedWhileRestrictBackgroundOn(int procState) {
+        return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+    }
+
+    /**
+     * @hide
+     */
+    public interface UidStateWithSeqObserver {
+        void onUidStateChangedWithSeq(int uid, int procState, long seq);
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1655847..2dfba28 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -844,6 +844,22 @@
             "android.settings.SYSTEM_UPDATE_SETTINGS";
 
     /**
+     * Activity Action: Show settings for managed profile settings.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGED_PROFILE_SETTINGS =
+            "android.settings.MANAGED_PROFILE_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of sync settings.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -9557,6 +9573,16 @@
         public static final String RETAIL_DEMO_MODE_CONSTANTS = "retail_demo_mode_constants";
 
         /**
+         * When blocked for the network policy rules to get updated, the maximum time that the
+         * {@link ActivityThread} have to wait before unblocking.
+         *
+         * Type: long
+         *
+         * @hide
+         */
+        public static final String WAIT_FOR_NETWORK_TIMEOUT_MS = "wait_for_network_timeout_ms";
+
+        /**
          * The reason for the settings database being downgraded. This is only for
          * troubleshooting purposes and its value should not be interpreted in any way.
          *
diff --git a/core/java/android/provider/SettingsStringUtil.java b/core/java/android/provider/SettingsStringUtil.java
index f242d79..3dfedea 100644
--- a/core/java/android/provider/SettingsStringUtil.java
+++ b/core/java/android/provider/SettingsStringUtil.java
@@ -60,7 +60,7 @@
             StringBuilder sb = new StringBuilder();
             Iterator<T> it = iterator();
             if (it.hasNext()) {
-                sb.append(it.next());
+                sb.append(itemToString(it.next()));
                 while (it.hasNext()) {
                     sb.append(DELIMITER);
                     sb.append(itemToString(it.next()));
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index 6da6a39..4099f59 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -30,7 +30,6 @@
 import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.util.Log;
-import android.view.autofill.FillResponse;
 
 import com.android.internal.os.SomeArgs;
 
@@ -70,7 +69,7 @@
     // Internal extras
     /** @hide */
     public static final String EXTRA_ACTIVITY_TOKEN =
-            "android.service.autofill.EXTRA_ACTIVITY_TOKEN";
+            "android.service.autofill.extra.ACTIVITY_TOKEN";
 
     // Handler messages.
     private static final int MSG_CONNECT = 1;
@@ -180,15 +179,16 @@
      * service.
      *
      * <p>Service must call one of the {@link FillCallback} methods (like
-     * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)})
+     * {@link FillCallback#onSuccess(FillResponse)}
+     * or {@link FillCallback#onFailure(CharSequence)})
      * to notify the result of the request.
      *
      * @param structure {@link Activity}'s view structure.
      * @param data bundle containing data passed by the service on previous calls to fill.
      *     This bundle allows your service to keep state between fill and save requests
      *     as well as when filling different sections of the UI as the system will try to
-     *     aggressively unbind from the service to conserve resources. See {@link FillResponse}
-     *     Javadoc for examples of multiple-sections requests.
+     *     aggressively unbind from the service to conserve resources. See {@link
+     *     FillResponse} Javadoc for examples of multiple-sections requests.
      * @param cancellationSignal signal for observing cancellation requests. The system will use
      *     this to notify you that the fill result is no longer needed and you should stop
      *     handling this fill request in order to save resources.
@@ -208,8 +208,8 @@
      * @param data bundle containing data passed by the service on previous calls to fill.
      *     This bundle allows your service to keep state between fill and save requests
      *     as well as when filling different sections of the UI as the system will try to
-     *     aggressively unbind from the service to conserve resources. See {@link FillResponse}
-     *     Javadoc for examples of multiple-sections requests.
+     *     aggressively unbind from the service to conserve resources. See {@link
+     *     FillResponse} Javadoc for examples of multiple-sections requests.
      * @param callback object used to notify the result of the request.
      */
     public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
diff --git a/core/java/android/view/autofill/Dataset.aidl b/core/java/android/service/autofill/Dataset.aidl
similarity index 94%
rename from core/java/android/view/autofill/Dataset.aidl
rename to core/java/android/service/autofill/Dataset.aidl
index 2a8e67c..2342c5f 100644
--- a/core/java/android/view/autofill/Dataset.aidl
+++ b/core/java/android/service/autofill/Dataset.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.view.autofill;
+package android.service.autofill;
 
-parcelable Dataset;
\ No newline at end of file
+parcelable Dataset;
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
similarity index 60%
rename from core/java/android/view/autofill/Dataset.java
rename to core/java/android/service/autofill/Dataset.java
index 2708358..bd38c7f 100644
--- a/core/java/android/view/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -14,59 +14,48 @@
  * limitations under the License.
  */
 
-package android.view.autofill;
-
-import static android.view.autofill.Helper.DEBUG;
+package android.service.autofill;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.assist.AssistStructure.ViewNode;
 import android.content.IntentSender;
-import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 
 /**
- * A set of data that can be used to auto-fill an {@link Activity}.
+ * A set of data that can be used to auto-fill an {@link android.app.Activity}.
  *
  * <p>It contains:
  *
  * <ol>
  *   <li>A name used to identify the dataset in the UI.
  *   <li>A list of id/value pairs for the fields that can be auto-filled.
- *   <li>An optional {@link Bundle} with extras (used only by the service creating it).
+ *   <li>A list of savable ids in addition to the ones with a provided value.
  * </ol>
  *
- * @see FillResponse for examples.
+ * @see android.service.autofill.FillResponse for examples.
  */
 public final class Dataset implements Parcelable {
-    private final String mId;
+    private static final boolean DEBUG = false;
+
     private final CharSequence mName;
     private final ArrayList<AutoFillId> mFieldIds;
     private final ArrayList<AutoFillValue> mFieldValues;
-    private final Bundle mExtras;
     private final IntentSender mAuthentication;
 
     private Dataset(Builder builder) {
-        mId = builder.mId;
         mName = builder.mName;
         mFieldIds = builder.mFieldIds;
         mFieldValues = builder.mFieldValues;
-        mExtras = builder.mExtras;
         mAuthentication = builder.mAuthentication;
     }
 
     /** @hide */
-    public @NonNull String getId() {
-        return mId;
-    }
-
-    /** @hide */
     public @NonNull CharSequence getName() {
         return mName;
     }
@@ -82,11 +71,6 @@
     }
 
     /** @hide */
-    public @Nullable Bundle getExtras() {
-        return mExtras;
-    }
-
-    /** @hide */
     public @Nullable IntentSender getAuthentication() {
         return mAuthentication;
     }
@@ -100,71 +84,32 @@
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("Dataset [id=").append(mId)
-                .append(", name=").append(mName)
+        final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName)
                 .append(", fieldIds=").append(mFieldIds)
                 .append(", fieldValues=").append(mFieldValues)
-                .append(", hasAuthentication=").append(mAuthentication != null)
-                .append(", hasExtras=").append(mExtras != null);
+                .append(", hasAuthentication=").append(mAuthentication != null);
         return builder.append(']').toString();
     }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final Dataset other = (Dataset) obj;
-        if (mId == null) {
-            if (other.mId != null) {
-                return false;
-            }
-        } else if (!mId.equals(other.mId)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return mId != null ? mId.hashCode() : 0;
-    }
-
     /**
      * A builder for {@link Dataset} objects. You must to provide at least
      * one value for a field or set an authentication intent.
      */
     public static final class Builder {
-        private String mId;
         private CharSequence mName;
         private ArrayList<AutoFillId> mFieldIds;
         private ArrayList<AutoFillValue> mFieldValues;
-        private Bundle mExtras;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
 
-        /** @hide */
-        // TODO(b/33197203): Remove once GCore migrates
-        public Builder(@NonNull CharSequence name) {
-            this(String.valueOf(System.currentTimeMillis()), name);
-        }
-
         /**
          * Creates a new builder.
          *
-         * @param id A required id to identify this dataset for future interactions related to it.
          * @param name Name used to identify the dataset in the UI. Typically it's the same value as
          * the first field in the dataset (like username or email address) or a user-provided name
          * (like "My Work Address").
          */
-        public Builder(@NonNull String id, @NonNull CharSequence name) {
-            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null");
+        public Builder(@NonNull CharSequence name) {
             mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null");
         }
 
@@ -172,50 +117,36 @@
          * Requires a dataset authentication before auto-filling the activity with this dataset.
          *
          * <p>This method is called when you need to provide an authentication
-         * UI for the dataset. For example, when a dataset contains credit card information
+         * UI for the data set. For example, when a data set contains credit card information
          * (such as number, expiration date, and verification code), you can display UI
          * asking for the verification code to before filing in the data). Even if the
-         * dataset is completely populated the system will launch the specified authentication
-         * intent and will need your approval to fill it in. Since the dataset is "locked"
-         * until the user authenticates it, typically this dataset name is masked
-         * (for example, "VISA....1234"). Typically you would want to store the dataset
-         * labels non-encypted and the actual sensitive data encrypted and not in memory.
+         * data set is completely populated the system will launch the specified authentication
+         * intent and will need your approval to fill it in. Since the data set is "locked"
+         * until the user authenticates it, typically this data set name is masked
+         * (for example, "VISA....1234"). Typically you would want to store the data set
+         * labels non-encrypted and the actual sensitive data encrypted and not in memory.
          * This allows showing the labels in the UI while involving the user if one of
          * the items with these labels is chosen. Note that if you use sensitive data as
-         * a label, for example an email address, then it should also be encrypted.
-         *</p>
+         * a label, for example an email address, then it should also be encrypted.</p>
          *
-         * <p>When a user selects this dataset, the system triggers the provided intent
-         * whose extras will have the {@link android.content.Intent#EXTRA_AUTO_FILL_ITEM_ID id}
-         * of the {@link android.view.autofill.Dataset dataset} to authenticate, the {@link
-         * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} associated with this
-         * dataset, and a {@link android.content.Intent#EXTRA_AUTO_FILL_CALLBACK callback}
-         * to dispatch the authentication result.</p>
+         * <p>When a user triggers auto-fill, the system launches the provided intent
+         * whose extras will have the {@link
+         * android.view.autofill.AutoFillManager#EXTRA_ASSIST_STRUCTURE screen content}. Once
+         * you complete your authentication flow you should set the activity result to {@link
+         * android.app.Activity#RESULT_OK} and provide the fully populated {@link Dataset
+         * dataset} by setting it to the {@link
+         * android.view.autofill.AutoFillManager#EXTRA_AUTHENTICATION_RESULT} extra. For example,
+         * if you provided an credit card information without the CVV for the data set in the
+         * {@link FillResponse response} then the returned data set should contain the
+         * CVV entry.</p>
          *
-         * <p>Once you complete your authentication flow you should use the provided callback
-         * to notify for a failure or a success. In case of a success you need to provide
-         * only the fully populated dataset that is being authenticated. For example, if you
-         * provided a {@link FillResponse} with two {@link Dataset}s and marked that
-         * only the first dataset needs an authentication then in the provided response
-         * you need to provide only the fully populated dataset being authenticated instead
-         * of both of them.
-         * </p>
-         *
-         * <p>The indent sender mechanism allows you to have your authentication UI
-         * implemented as an activity or a service or a receiver. However, the recommended
-         * way is to do this is with an activity which the system will start in the
-         * filled activity's task meaning it will properly work with back, recent apps, and
-         * free-form multi-window, while avoiding the need for the "draw on top of other"
-         * apps special permission. You can still theme your authentication activity's
-         * UI to look like a dialog if desired.</p>
-         *
-         * <p></><strong>Note:</strong> Do not make the provided intent sender
+         * <p></><strong>Note:</strong> Do not make the provided pending intent
          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
          * platform needs to fill in the authentication arguments.</p>
          *
-         * @param authentication Intent to trigger your authentication flow.
+         * @param authentication Intent to an activity with your authentication flow.
          *
-         * @see android.app.PendingIntent#getIntentSender()
+         * @see android.app.PendingIntent
          */
         public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
             throwIfDestroyed();
@@ -226,7 +157,8 @@
         /**
          * Sets the value of a field.
          *
-         * @param id id returned by {@link ViewNode#getAutoFillId()}.
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutoFillId()}.
          * @param value value to be auto filled.
          */
         public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
@@ -249,18 +181,6 @@
         }
 
         /**
-         * Sets a {@link Bundle} that will be passed to subsequent APIs that
-         * manipulate this dataset. For example, they are passed in as {@link
-         * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your
-         * authentication flow.
-         */
-        public @NonNull Builder setExtras(@Nullable Bundle extras) {
-            throwIfDestroyed();
-            mExtras = extras;
-            return this;
-        }
-
-        /**
          * Creates a new {@link Dataset} instance. You should not interact
          * with this builder once this method is called.
          */
@@ -292,21 +212,19 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeString(mId);
         parcel.writeCharSequence(mName);
         parcel.writeTypedArrayList(mFieldIds, 0);
         parcel.writeTypedArrayList(mFieldValues, 0);
-        parcel.writeBundle(mExtras);
         parcel.writeParcelable(mAuthentication, flags);
     }
 
-    public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() {
+    public static final Creator<Dataset> CREATOR = new Creator<Dataset>() {
         @Override
         public Dataset createFromParcel(Parcel parcel) {
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = new Builder(parcel.readString(), parcel.readCharSequence());
+            final Builder builder = new Builder(parcel.readCharSequence());
             final ArrayList<AutoFillId> ids = parcel.readTypedArrayList(null);
             final ArrayList<AutoFillValue> values = parcel.readTypedArrayList(null);
             final int idCount = (ids != null) ? ids.size() : 0;
@@ -316,7 +234,6 @@
                 AutoFillValue value = (valueCount > i) ? values.get(i) : null;
                 builder.setValue(id, value);
             }
-            builder.setExtras(parcel.readBundle());
             builder.setAuthentication(parcel.readParcelable(null));
             return builder.build();
         }
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index a306809..69c9904 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -19,16 +19,13 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.os.RemoteException;
-import android.view.autofill.FillResponse;
 
 /**
  * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
  * auto-filled.
  */
-public final class FillCallback implements Parcelable {
+public final class FillCallback {
     private final IFillCallback mCallback;
     private boolean mCalled;
 
@@ -37,11 +34,6 @@
         mCallback = callback;
     }
 
-    /** @hide */
-    private FillCallback(Parcel parcel) {
-        mCallback = IFillCallback.Stub.asInterface(parcel.readStrongBinder());
-    }
-
     /**
      * Notifies the Android System that an
      * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
@@ -79,33 +71,9 @@
         }
     }
 
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeStrongBinder(mCallback.asBinder());
-    }
-
     private void assertNotCalled() {
         if (mCalled) {
             throw new IllegalStateException("Already called");
         }
     }
-
-    public static final Creator<FillCallback> CREATOR = new Creator<FillCallback>() {
-        @Override
-        public FillCallback createFromParcel(Parcel parcel) {
-            return new FillCallback(parcel);
-        }
-
-        @Override
-        public FillCallback[] newArray(int size) {
-            return new FillCallback[size];
-        }
-    };
 }
diff --git a/core/java/android/view/autofill/FillResponse.aidl b/core/java/android/service/autofill/FillResponse.aidl
similarity index 94%
rename from core/java/android/view/autofill/FillResponse.aidl
rename to core/java/android/service/autofill/FillResponse.aidl
index b018f15..e9c2d21 100644
--- a/core/java/android/view/autofill/FillResponse.aidl
+++ b/core/java/android/service/autofill/FillResponse.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.view.autofill;
+package android.service.autofill;
 
-parcelable FillResponse;
\ No newline at end of file
+parcelable FillResponse;
diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
similarity index 73%
rename from core/java/android/view/autofill/FillResponse.java
rename to core/java/android/service/autofill/FillResponse.java
index 596a06c..ea36e64 100644
--- a/core/java/android/view/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -13,30 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.view.autofill;
-
-import static android.view.autofill.Helper.DEBUG;
+package android.service.autofill;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
-
-import com.android.internal.util.Preconditions;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillManager;
 
 /**
  * Response for a {@link
- * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} and
+ * AutoFillService#onFillRequest(android.app.assist.AssistStructure,
+ * Bundle, android.os.CancellationSignal, FillCallback)} and
  * authentication requests.
  *
  * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
  * fields that can be auto-filled together, and the Android system displays a dataset picker UI
- * affordance that the user must use before the {@link Activity} is filled with the dataset.
+ * affordance that the user must use before the {@link android.app.Activity} is filled with
+ * the dataset.
  *
  * <p>For example, for a login page with username/password where the user only has one account in
  * the response could be:
@@ -65,9 +63,9 @@
  *      .build();
  * </pre>
  *
- * <p>If the user does not have any data associated with this {@link Activity} but the service wants
- * to offer the user the option to save the data that was entered, then the service could populate
- * the response with {@code savableIds} instead of {@link Dataset}s:
+ * <p>If the user does not have any data associated with this {@link android.app.Activity} but
+ * the service wants to offer the user the option to save the data that was entered, then the
+ * service could populate the response with {@code savableIds} instead of {@link Dataset}s:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
@@ -142,7 +140,7 @@
  * #setAuthentication(IntentSender)} and {@link Dataset.Builder#setAuthentication(IntentSender)}.
  * It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
  * which would allow you to provide the dataset names to the user and if they choose one
- * them challenge the user to authenticate. For example, if the user has a home and a work
+ * them challenge the user to onAuthenticate. For example, if the user has a home and a work
  * address the Home and Work labels could be stored unencrypted as they don't have any sensitive
  * data while the address data is in an encrypted storage. If the user chooses Home, then the
  * platform will start your authentication flow. If you encrypt all data and require auth
@@ -152,20 +150,16 @@
  * Work options where they can pick one. Hence, you have flexibility how to implement your
  * auth while storing labels non-encrypted and data encrypted provides a better user
  * experience.</p>
- *
- * <p>Finally, the service can use {@link Dataset.Builder#setExtras(Bundle)} methods
- * to pass {@link Bundle extras} provided to all future calls related to a dataset,
- * for example during authentication and saving.</p>
  */
 public final class FillResponse implements Parcelable {
-    private final String mId;
+    private static final boolean DEBUG = false;
+
     private final ArraySet<Dataset> mDatasets;
     private final ArraySet<AutoFillId> mSavableIds;
     private final Bundle mExtras;
     private final IntentSender mAuthentication;
 
     private FillResponse(@NonNull Builder builder) {
-        mId = builder.mId;
         mDatasets = builder.mDatasets;
         mSavableIds = builder.mSavableIds;
         mExtras = builder.mExtras;
@@ -173,11 +167,6 @@
     }
 
     /** @hide */
-    public @NonNull String getId() {
-        return mId;
-    }
-
-    /** @hide */
     public @Nullable Bundle getExtras() {
         return mExtras;
     }
@@ -202,71 +191,49 @@
      * one dataset or set an authentication intent.
      */
     public static final class Builder {
-        private final String mId;
         private ArraySet<Dataset> mDatasets;
         private ArraySet<AutoFillId> mSavableIds;
         private Bundle mExtras;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
 
-        /** @hide */
-        // TODO(b/33197203): Remove once GCore migrates
-        public Builder() {
-            this(String.valueOf(System.currentTimeMillis()));
-        }
-
         /**
          * Creates a new {@link FillResponse} builder.
-         *
-         * @param id A required id to identify this dataset for future interactions related to it.
          */
-        public Builder(@NonNull String id) {
-            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null");
+        public Builder() {
+
         }
 
         /**
          * Requires a fill response authentication before auto-filling the activity with
-         * any dataset in this response. This is typically useful when a user interaction
-         * is required to unlock their data vault if you encrypt the dataset labels and
-         * dataset data. It is recommended to encrypt only the sensitive data and not the
-         * dataset labels which would allow auth on the dataset level leading to a better
-         * user experience. Note that if you use sensitive data as a label, for example an
-         * email address, then it should also be encrypted.
+         * any data set in this response.
          *
-         * <p>This method is called when you need to provide an authentication
-         * UI for the fill response. For example, when the user's data is stored
-         * encrypted and needs a user interaction to decrypt before offering fill
-         * suggestions.</p>
+         * <p>This is typically useful when a user interaction is required to unlock their
+         * data vault if you encrypt the data set labels and data set data. It is recommended
+         * to encrypt only the sensitive data and not the data set labels which would allow
+         * auth on the data set level leading to a better user experience. Note that if you
+         * use sensitive data as a label, for example an email address, then it should also
+         * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
+         * activity which implements your authentication flow.</p>
          *
-         * <p>When a user initiates an auto fill, the system triggers the provided
-         * intent whose extras will have the {@link android.content.Intent
-         * #EXTRA_AUTO_FILL_ITEM_ID id} of the {@link android.view.autofill.FillResponse})
-         * to authenticate, the {@link android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras}
-         * associated with this response, and a {@link android.content.Intent
-         * #EXTRA_AUTO_FILL_CALLBACK callback} to dispatch the authentication result.</p>
+         * <p>When a user triggers auto-fill, the system launches the provided intent
+         * whose extras will have the {@link
+         * AutoFillManager#EXTRA_ASSIST_STRUCTURE screen
+         * content}. Once you complete your authentication flow you should set the activity
+         * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated {@link
+         * FillResponse response} by setting it to the {@link
+         * AutoFillManager#EXTRA_AUTHENTICATION_RESULT} extra.
+         * For example, if you provided an empty {@link FillResponse resppnse} because the
+         * user's data was locked and marked that the response needs an authentication then
+         * in the response returned if authentication succeeds you need to provide all
+         * available data sets some of which may need to be further authenticated, for
+         * example a credit card whose CVV needs to be entered.</p>
          *
-         * <p>Once you complete your authentication flow you should use the provided callback
-         * to notify for a failure or a success. In case of a success you need to provide
-         * the fully populated response that is being authenticated. For example, if you
-         * provided an empty {@link FillResponse} because the user's data was locked and
-         * marked that the response needs an authentication then in the response returned
-         * if authentication succeeds you need to provide all available datasets some of
-         * which may need to be further authenticated, for example a credit card whose
-         * CVV needs to be entered.</p>
-         *
-         * <p>The indent sender mechanism allows you to have your authentication UI
-         * implemented as an activity or a service or a receiver. However, the recommended
-         * way is to do this is with an activity which the system will start in the
-         * filled activity's task meaning it will properly work with back, recent apps, and
-         * free-form multi-window, while avoiding the need for the "draw on top of other"
-         * apps special permission. You can still theme your authentication activity's
-         * UI to look like a dialog if desired.</p>
-         *
-         * <p></><strong>Note:</strong> Do not make the provided intent sender
+         * <p></><strong>Note:</strong> Do not make the provided pending intent
          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
          * platform needs to fill in the authentication arguments.</p>
          *
-         * @param authentication Intent to trigger your authentication flow.
+         * @param authentication Intent to an activity with your authentication flow.
          *
          * @see android.app.PendingIntent#getIntentSender()
          */
@@ -313,8 +280,8 @@
 
         /**
          * Adds ids of additional fields that the service would be interested to save (through
-         * {@link android.service.autofill.AutoFillService#onSaveRequest(
-         * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)})
+         * {@link AutoFillService#onSaveRequest(
+         * android.app.assist.AssistStructure, Bundle, SaveCallback)})
          * but were not indirectly set through {@link #addDataset(Dataset)}.
          *
          * <p>See {@link FillResponse} for examples.
@@ -335,15 +302,13 @@
 
         /**
          * Sets a {@link Bundle} that will be passed to subsequent APIs that
-         * manipulate this response. For example, they are passed in as {@link
-         * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your
-         * authentication flow and to subsequent calls to {@link
-         * android.service.autofill.AutoFillService#onFillRequest(
+         * manipulate this response. For example, they are passed to subsequent
+         * calls to {@link AutoFillService#onFillRequest(
          * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
-         * android.service.autofill.FillCallback)} and {@link
-         * android.service.autofill.AutoFillService#onSaveRequest(
+         * FillCallback)} and {@link
+         * AutoFillService#onSaveRequest(
          * android.app.assist.AssistStructure, Bundle,
-         * android.service.autofill.SaveCallback)}.
+         * SaveCallback)}.
          */
         public Builder setExtras(Bundle extras) {
             throwIfDestroyed();
@@ -374,8 +339,7 @@
     public String toString() {
         if (!DEBUG) return super.toString();
         final StringBuilder builder = new StringBuilder(
-                "FillResponse: [id=").append(mId)
-                .append(", datasets=").append(mDatasets)
+                "FillResponse: [datasets=").append(mDatasets)
                 .append(", savableIds=").append(mSavableIds)
                 .append(", hasExtras=").append(mExtras != null)
                 .append(", hasAuthentication=").append(mAuthentication != null);
@@ -393,7 +357,6 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeString(mId);
         parcel.writeTypedArraySet(mDatasets, 0);
         parcel.writeTypedArraySet(mSavableIds, 0);
         parcel.writeParcelable(mExtras, 0);
@@ -407,7 +370,7 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = new Builder(parcel.readString());
+            final Builder builder = new Builder();
             final ArraySet<Dataset> datasets = parcel.readTypedArraySet(null);
             final int datasetCount = (datasets != null) ? datasets.size() : 0;
             for (int i = 0; i < datasetCount; i++) {
diff --git a/core/java/android/service/autofill/IAuthenticationCallback.aidl b/core/java/android/service/autofill/IAuthenticationCallback.aidl
new file mode 100644
index 0000000..36b989d
--- /dev/null
+++ b/core/java/android/service/autofill/IAuthenticationCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.view.autofill;
+
+import android.view.autofill.Dataset;
+import android.service.autofill.FillResponse;
+
+/**
+ * Callback for delivering authentication result.
+ *
+ * {@hide}
+ */
+interface IAutoFillAuthCallback {
+    void onSuccessForDataset(in Dataset dataset);
+    void onSuccessForFillResponse(in FillResponse response);
+    void onFailure(CharSequence message);
+}
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
deleted file mode 100644
index 6cdb516..0000000
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.autofill;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillValue;
-
-/**
- * Mediator between apps being auto-filled and auto-fill service implementations.
- *
- * {@hide}
- */
-oneway interface IAutoFillManagerService {
-    // Methods called by AutoFillManager
-    void startSession(in IBinder activityToken, in IBinder appCallback, in AutoFillId autoFillId,
-                      in Rect bounds, in AutoFillValue value);
-    void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
-                       in AutoFillValue value, int flags);
-    void finishSession(in IBinder activityToken);
-}
diff --git a/core/java/android/service/autofill/IFillCallback.aidl b/core/java/android/service/autofill/IFillCallback.aidl
index 537403e..2bb3e9a 100644
--- a/core/java/android/service/autofill/IFillCallback.aidl
+++ b/core/java/android/service/autofill/IFillCallback.aidl
@@ -18,7 +18,7 @@
 
 import android.os.ICancellationSignal;
 
-import android.view.autofill.FillResponse;
+import android.service.autofill.FillResponse;
 
 /**
  * Interface to receive the result of a save request.
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 46b3072..c6dd1df 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -23,8 +23,6 @@
 /**
  * Handles save requests from the {@link AutoFillService} into the {@link Activity} being
  * auto-filled.
- *
- * <p>This class is thread safe.
  */
 public final class SaveCallback {
     private final ISaveCallback mCallback;
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 3048a38..82e44dc 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -170,14 +170,24 @@
         private final int mWeight;
         private final boolean mIsItalic;
         private ParcelFileDescriptor mFd;
+        private final int mResourceId;
 
-        public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
+        /**
+         * @hide
+         */
+        public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic,
+                int resourceId) {
             mFontName = fontName;
             mTtcIndex = ttcIndex;
             mAxes = axes;
             mWeight = weight;
             mIsItalic = isItalic;
             mFd = null;
+            mResourceId = resourceId;
+        }
+
+        public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
+            this(fontName, ttcIndex, axes, weight, isItalic, 0);
         }
 
         public Font(Font origin) {
@@ -193,6 +203,7 @@
                     e.printStackTrace();
                 }
             }
+            mResourceId = origin.mResourceId;
         }
 
         /**
@@ -254,6 +265,13 @@
         /**
          * @hide
          */
+        public int getResourceId() {
+            return mResourceId;
+        }
+
+        /**
+         * @hide
+         */
         public Font(Parcel in) {
             mFontName = in.readString();
             mTtcIndex = in.readInt();
@@ -269,6 +287,7 @@
             } else {
                 mFd = null;
             }
+            mResourceId = in.readInt();
         }
 
         @Override
@@ -285,6 +304,7 @@
             if (mFd != null) {
                 mFd.writeToParcel(out, flag);
             }
+            out.writeInt(mResourceId);
         }
 
         @Override
@@ -382,22 +402,40 @@
         private final List<Font> mFonts;
         private final String mLanguage;
         private final String mVariant;
+        private final String mProviderAuthority;
+        private final String mQuery;
 
         public Family(String name, List<Font> fonts, String language, String variant) {
-            this.mName = name;
-            this.mFonts = fonts;
-            this.mLanguage = language;
-            this.mVariant = variant;
+            mName = name;
+            mFonts = fonts;
+            mLanguage = language;
+            mVariant = variant;
+            mProviderAuthority = null;
+            mQuery = null;
+        }
+
+        /**
+         * @hide
+         */
+        public Family(String providerAuthority, String query) {
+            mName = null;
+            mFonts = null;
+            mLanguage = null;
+            mVariant = null;
+            mProviderAuthority = providerAuthority;
+            mQuery = query;
         }
 
         public Family(Family origin) {
-            this.mName = origin.mName;
-            this.mLanguage = origin.mLanguage;
-            this.mVariant = origin.mVariant;
-            this.mFonts = new ArrayList<>();
+            mName = origin.mName;
+            mLanguage = origin.mLanguage;
+            mVariant = origin.mVariant;
+            mFonts = new ArrayList<>();
             for (int i = 0; i < origin.mFonts.size(); i++) {
                 mFonts.add(new Font(origin.mFonts.get(i)));
             }
+            mProviderAuthority = origin.mProviderAuthority;
+            mQuery = origin.mQuery;
         }
 
         /**
@@ -431,6 +469,20 @@
         /**
          * @hide
          */
+        public String getProviderAuthority() {
+            return mProviderAuthority;
+        }
+
+        /**
+         * @hide
+         */
+        public String getQuery() {
+            return mQuery;
+        }
+
+        /**
+         * @hide
+         */
         public Family(Parcel in) {
             mName = in.readString();
             final int size = in.readInt();
@@ -440,6 +492,16 @@
             }
             mLanguage = in.readString();
             mVariant = in.readString();
+            if (in.readInt() == 1) {
+                mProviderAuthority = in.readString();
+            } else {
+                mProviderAuthority = null;
+            }
+            if (in.readInt() == 1) {
+                mQuery = in.readString();
+            } else {
+                mQuery = null;
+            }
         }
 
         @Override
@@ -451,6 +513,14 @@
             }
             out.writeString(mLanguage);
             out.writeString(mVariant);
+            out.writeInt(mProviderAuthority == null ? 0 : 1);
+            if (mProviderAuthority != null) {
+                out.writeString(mProviderAuthority);
+            }
+            out.writeInt(mQuery == null ? 0 : 1);
+            if (mQuery != null) {
+                out.writeString(mQuery);
+            }
         }
 
         @Override
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 41c44f1..7af1020 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -132,7 +132,7 @@
     }
 
     public void setSurfaceTexture(SurfaceTexture surface) {
-        nSetSurfaceTexture(mFinalizer.get(), surface, false);
+        nSetSurfaceTexture(mFinalizer.get(), surface);
         mRenderer.pushLayerUpdate(this);
     }
 
@@ -148,7 +148,6 @@
     private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
     private static native void nSetLayerPaint(long layerUpdater, long paint);
     private static native void nSetTransform(long layerUpdater, long matrix);
-    private static native void nSetSurfaceTexture(long layerUpdater,
-            SurfaceTexture surface, boolean isAlreadyAttached);
+    private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface);
     private static native void nUpdateSurfaceTexture(long layerUpdater);
 }
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 6e2a92c..7a5e670 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -282,6 +282,25 @@
     public abstract void clearLastInputMethodWindowForTransition();
 
     /**
+     * Notifies WindowManagerService that the current IME window status is being changed.
+     *
+     * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
+     * caller of this method.</p>
+     *
+     * @param imeToken token to track the active input method. Corresponding IME windows can be
+     *                 identified by checking {@link android.view.WindowManager.LayoutParams#token}.
+     *                 Note that there is no guarantee that the corresponding window is already
+     *                 created
+     * @param imeWindowVisible whether the active IME thinks that its window should be visible or
+     *                         hidden, no matter how WindowManagerService will react / has reacted
+     *                         to corresponding API calls.  Note that this state is not guaranteed
+     *                         to be synchronized with state in WindowManagerService.
+     * @param targetWindowToken token to identify the target window that the IME is associated with.
+     */
+    public abstract void updateInputMethodWindowStatus(IBinder imeToken, boolean imeWindowVisible,
+            IBinder targetWindowToken);
+
+    /**
       * Returns true when the hardware keyboard is available.
       */
     public abstract boolean isHardKeyboardAvailable();
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index 58607ba..f7a1b61 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -16,40 +16,101 @@
 
 package android.view.autofill;
 
-import static android.view.autofill.Helper.VERBOSE;
-
-import android.annotation.Nullable;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Parcelable;
 import android.os.RemoteException;
-import android.service.autofill.IAutoFillManagerService;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.View;
 
+import java.lang.ref.WeakReference;
+import java.util.List;
+
 /**
  * App entry point to the AutoFill Framework.
  */
 // TODO(b/33197203): improve this javadoc
 //TODO(b/33197203): restrict manager calls to activity
 public final class AutoFillManager {
+    private static final boolean DEBUG = false;
 
     private static final String TAG = "AutoFillManager";
 
+    /**
+     * Intent extra: The assist structure which captures the filled screen.
+     * <p>
+     * Type: {@link android.app.assist.AssistStructure}
+     * </p>
+     */
+    public static final String EXTRA_ASSIST_STRUCTURE =
+            "android.view.autofill.extra.ASSIST_STRUCTURE";
+
+    /**
+     * Intent extra: The result of an authentication operation. It is
+     * either a fully populated {@link android.service.autofill.FillResponse}
+     * or a fully populated {@link android.service.autofill.Dataset} if
+     * a response or a dataset is being authenticated respectively.
+     *
+     * <p>
+     * Type: {@link android.service.autofill.FillResponse} or a
+     * {@link android.service.autofill.Dataset}
+     * </p>
+     */
+    public static final String EXTRA_AUTHENTICATION_RESULT =
+            "android.view.autofill.extra.AUTHENTICATION_RESULT";
+
     /** @hide */ public static final int FLAG_START_SESSION = 0x1;
     /** @hide */ public static final int FLAG_FOCUS_GAINED = 0x2;
     /** @hide */ public static final int FLAG_FOCUS_LOST = 0x4;
     /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x8;
 
-    private final IAutoFillManagerService mService;
-    private final Context mContext;
+    // These are activities that may have auto-fill UI which are keyed off their tokens.
+    // This is done instead of the activity setting the client in the auto-fill manager
+    // to avoid unnecessary instantiation of the manager and do this only if there is an
+    // auto-fillable focused. This has only the cost of loading the class vs creating an
+    // auto-fill manager for every activity even one that cannot be filled.
+    private static final ArrayMap<IBinder, AutoFillClient> sPendingClients = new ArrayMap<>();
 
-    private AutoFillSession mSession;
+    private final Rect mTempRect = new Rect();
+
+    private final IAutoFillManager mService;
+    private IAutoFillManagerClient mServiceClient;
+
+    private Context mContext;
+
+    private AutoFillClient mClient;
+
+    private boolean mHasSession;
+    private boolean mEnabled;
+
+    /** @hide */
+    public interface AutoFillClient {
+        /**
+         * Asks the client to perform an auto-fill.
+         *
+         * @param ids The values to auto-fill
+         * @param values The values to auto-fill
+         */
+        void autoFill(List<AutoFillId> ids, List<AutoFillValue> values);
+
+        /**
+         * Asks the client to start an authentication flow.
+         *
+         * @param intent The authentication intent.
+         * @param fillInIntent The authentication fill-in intent.
+         */
+        void authenticate(IntentSender intent, Intent fillInIntent);
+    }
 
     /**
      * @hide
      */
-    public AutoFillManager(Context context, IAutoFillManagerService service) {
+    public AutoFillManager(Context context, IAutoFillManager service) {
         mContext = context;
         mService = service;
     }
@@ -61,27 +122,26 @@
      * @param gainFocus whether focus was gained or lost.
      */
     public void focusChanged(View view, boolean gainFocus) {
-        if (mSession == null) {
-            // Starts new session.
-            final Rect bounds = new Rect();
-            view.getBoundsOnScreen(bounds);
-            final AutoFillId id = getAutoFillId(view);
-            final AutoFillValue value = view.getAutoFillValue();
-            startSession(id, bounds, value);
+        ensureServiceClientAddedIfNeeded();
+
+        if (!mEnabled) {
             return;
         }
 
-        if (!mSession.isEnabled()) {
-            // Auto-fill is disabled for this session.
-            return;
-        }
-
-        // Update focus on existing session.
-        final Rect bounds = new Rect();
+        final Rect bounds = mTempRect;
         view.getBoundsOnScreen(bounds);
         final AutoFillId id = getAutoFillId(view);
         final AutoFillValue value = view.getAutoFillValue();
-        updateSession(id, bounds, value, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST);
+
+        if (!mHasSession) {
+            if (gainFocus) {
+                // Starts new session.
+                startSession(id, bounds, value);
+            }
+        } else {
+            // Update focus on existing session.
+            updateSession(id, bounds, value, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST);
+        }
     }
 
     /**
@@ -93,21 +153,23 @@
      * @param gainFocus whether focus was gained or lost.
      */
     public void virtualFocusChanged(View parent, int childId, Rect bounds, boolean gainFocus) {
-        if (mSession == null) {
-            // Starts new session.
-            final AutoFillId id = getAutoFillId(parent, childId);
-            startSession(id, bounds, null);
+        ensureServiceClientAddedIfNeeded();
+
+        if (!mEnabled) {
             return;
         }
 
-        if (!mSession.isEnabled()) {
-            // Auto-fill is disabled for this session.
-            return;
-        }
-
-        // Update focus on existing session.
         final AutoFillId id = getAutoFillId(parent, childId);
-        updateSession(id, bounds, null, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST);
+
+        if (!mHasSession) {
+            if (gainFocus) {
+                // Starts new session.
+                startSession(id, bounds, null);
+            }
+        } else {
+            // Update focus on existing session.
+            updateSession(id, bounds, null, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST);
+        }
     }
 
     /**
@@ -116,7 +178,11 @@
      * @param view view whose focus changed.
      */
     public void valueChanged(View view) {
-        if (mSession == null) return;
+        ensureServiceClientAddedIfNeeded();
+
+        if (!mEnabled || !mHasSession) {
+            return;
+        }
 
         final AutoFillId id = getAutoFillId(view);
         final AutoFillValue value = view.getAutoFillValue();
@@ -132,7 +198,11 @@
      * @param value new value of the child.
      */
     public void virtualValueChanged(View parent, int childId, AutoFillValue value) {
-        if (mSession == null) return;
+        ensureServiceClientAddedIfNeeded();
+
+        if (!mEnabled || !mHasSession) {
+            return;
+        }
 
         final AutoFillId id = getAutoFillId(parent, childId);
         updateSession(id, null, value, FLAG_VALUE_CHANGED);
@@ -145,30 +215,51 @@
      * call this method after the form is submitted and another page is rendered.
      */
     public void reset() {
-        if (mSession == null) return;
+        ensureServiceClientAddedIfNeeded();
 
-        final IBinder activityToken = mSession.mToken.get();
-        if (activityToken == null) {
-            Log.wtf(TAG, "finishSession(): token already GC'ed");
+        if (!mEnabled && !mHasSession) {
             return;
         }
-        try {
-            mService.finishSession(activityToken);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mSession = null;
-        }
+
+        finishSession();
     }
 
-    /**
-     * Gets the current session, if any.
-     *
-     * @hide
-     */
-    @Nullable
-    public AutoFillSession getSession() {
-        return mSession;
+    /** @hide */
+    public static void addClient(IBinder token, AutoFillClient client) {
+        sPendingClients.put(token, client);
+    }
+
+    /** @hide */
+    public static boolean isClientActive(IBinder token) {
+        return !sPendingClients.containsKey(token);
+    }
+
+    private void activateClient() {
+        mClient = sPendingClients.remove(mContext.getActivityToken());
+    }
+
+    private AutoFillClient getClient() {
+        if (mClient == null) {
+            return sPendingClients.get(mContext.getActivityToken());
+        }
+        return mClient;
+    }
+
+    /** @hide */
+    public void onAuthenticationResult(Intent data) {
+        if (data == null) {
+            return;
+        }
+        Parcelable result = data.getParcelableExtra(
+                EXTRA_AUTHENTICATION_RESULT);
+        Bundle responseData = new Bundle();
+        responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
+        try {
+            mService.setAuthenticationResult(responseData,
+                    mContext.getActivityToken(), mContext.getUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error delivering authentication result", e);
+        }
     }
 
     private AutoFillId getAutoFillId(View view) {
@@ -180,34 +271,98 @@
     }
 
     private void startSession(AutoFillId id, Rect bounds, AutoFillValue value) {
-        if (VERBOSE) {
+        if (DEBUG) {
             Log.v(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
         }
-
-        final IBinder activityToken = mContext.getActivityToken();
-        mSession = new AutoFillSession(this, activityToken);
-        final IBinder appCallback = mSession.getCallback().asBinder();
         try {
-            mService.startSession(activityToken, appCallback, id, bounds, value);
+            mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(),
+                    id, bounds, value, mContext.getUserId());
+            mHasSession = true;
+            activateClient();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void finishSession() {
+        if (DEBUG) {
+            Log.v(TAG, "finishSession()");
+        }
+        mHasSession = false;
+        try {
+            mService.finishSession(mContext.getActivityToken(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     private void updateSession(AutoFillId id, Rect bounds, AutoFillValue value, int flags) {
-        if (VERBOSE) {
+        if (DEBUG) {
             Log.v(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
                     + ", flags=" + flags);
         }
-
-        final IBinder activityToken = mSession.mToken.get();
-        if (activityToken == null) {
-            return;
-        }
         try {
-            mService.updateSession(activityToken, id, bounds, value, flags);
+            mService.updateSession(mContext.getActivityToken(), id, bounds, value, flags,
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private void ensureServiceClientAddedIfNeeded() {
+        if (getClient() == null) {
+            return;
+        }
+        if (mServiceClient == null) {
+            mServiceClient = new AutoFillManagerClient(this);
+            try {
+                mEnabled = mService.addClient(mServiceClient, mContext.getUserId());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private static final class AutoFillManagerClient extends IAutoFillManagerClient.Stub {
+        private final WeakReference<AutoFillManager> mAutoFillManager;
+
+        AutoFillManagerClient(AutoFillManager autoFillManager) {
+            mAutoFillManager = new WeakReference<>(autoFillManager);
+        }
+
+        @Override
+        public void setState(boolean enabled) {
+            final AutoFillManager autoFillManager = mAutoFillManager.get();
+            if (autoFillManager != null) {
+                autoFillManager.mContext.getMainThreadHandler().post(() ->
+                        autoFillManager.mEnabled = enabled);
+            }
+        }
+
+        @Override
+        public void autoFill(List<AutoFillId> ids, List<AutoFillValue> values) {
+            // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
+            // dataset.extras to service
+            final AutoFillManager autoFillManager = mAutoFillManager.get();
+            if (autoFillManager != null) {
+                autoFillManager.mContext.getMainThreadHandler().post(() -> {
+                    if (autoFillManager.getClient() != null) {
+                        autoFillManager.getClient().autoFill(ids, values);
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void authenticate(IntentSender intent, Intent fillInIntent) {
+            final AutoFillManager autoFillManager = mAutoFillManager.get();
+            if (autoFillManager != null) {
+                autoFillManager.mContext.getMainThreadHandler().post(() -> {
+                    if (autoFillManager.getClient() != null) {
+                        autoFillManager.getClient().authenticate(intent, fillInIntent);
+                    }
+                });
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/autofill/AutoFillSession.java b/core/java/android/view/autofill/AutoFillSession.java
deleted file mode 100644
index 64df62f..0000000
--- a/core/java/android/view/autofill/AutoFillSession.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2017 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.view.autofill;
-
-import static android.view.autofill.Helper.DEBUG;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.IBinder;
-import android.service.autofill.IAutoFillAppCallback;
-import android.util.Log;
-import android.view.View;
-
-import java.lang.ref.WeakReference;
-
-/**
- * An auto-fill session associated with an activity.
- *
- * @hide
- */
-public final class AutoFillSession {
-
-    private static final String TAG = "AutoFillSession";
-
-    private final IAutoFillAppCallback mCallback = new IAutoFillAppCallback.Stub() {
-
-        @Override
-        public void enableSession() {
-            if (DEBUG) Log.d(TAG, "enableSession()");
-
-            mEnabled = true;
-        }
-
-        @Override
-        public void autoFill(Dataset dataset) {
-            final Activity activity = mActivity.get();
-            if (activity == null) {
-                if (DEBUG) Log.d(TAG, "autoFill(): activity already GCed");
-                return;
-            }
-            // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
-            // dataset.extras to service
-            activity.runOnUiThread(() -> {
-                final View root = activity.getWindow().getDecorView().getRootView();
-                final int itemCount = dataset.getFieldIds().size();
-                for (int i = 0; i < itemCount; i++) {
-                    final AutoFillId id = dataset.getFieldIds().get(i);
-                    final AutoFillValue value = dataset.getFieldValues().get(i);
-                    final int viewId = id.getViewId();
-                    final View view = root.findViewByAccessibilityIdTraversal(viewId);
-                    if (view == null) {
-                        Log.w(TAG, "autoFill(): no View with id " + viewId);
-                        continue;
-                    }
-
-                    if (id.isVirtual()) {
-                        view.autoFillVirtual(id.getVirtualChildId(), value);
-                    } else {
-                        view.autoFill(value);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void startIntentSender(IntentSender intent, Intent fillInIntent) {
-            final Activity activity = mActivity.get();
-            if (activity != null) {
-                activity.runOnUiThread(() -> {
-                    try {
-                        activity.startIntentSender(intent, fillInIntent, 0, 0, 0);
-                    } catch (IntentSender.SendIntentException e) {
-                        Log.e(TAG, "startIntentSender() failed for intent:" + intent, e);
-                    }
-                });
-            }
-        }
-    };
-
-    private final AutoFillManager mAfm;
-    private WeakReference<Activity> mActivity;
-
-    // Reference to the token, which is used by the server.
-    final WeakReference<IBinder> mToken;
-
-    private boolean mEnabled;
-
-    public AutoFillSession(AutoFillManager afm, IBinder token) {
-        mToken = new WeakReference<>(token);
-        mAfm = afm;
-    }
-
-    /**
-     * Called by the {@link Activity} when it was asked to provider auto-fill data.
-     */
-    public void attachActivity(Activity activity) {
-        if (mActivity != null) {
-            Log.w(TAG, "attachActivity(): already attached");
-            return;
-        }
-        mActivity = new WeakReference<>(activity);
-    }
-
-    /**
-     * Checks whether auto-fill is enabled for this session, as decided by the
-     * {@code AutoFillManagerService}.
-     */
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    /**
-     * Notifies the manager that a session finished.
-     */
-    // TODO(b/33197203): hook it to other lifecycle events like fragments transition
-    public void finishSession() {
-        if (mAfm != null) {
-            try {
-                mAfm.reset();
-            } catch (RuntimeException e) {
-                Log.w(TAG, "Failed to finish session for " + mToken.get() + ": " + e);
-            }
-        }
-    }
-
-    public IAutoFillAppCallback getCallback() {
-        return mCallback;
-    }
-
-    @Override
-    public String toString() {
-        if (!DEBUG) return super.toString();
-
-        return "AutoFillSession[activityoken=" + mToken.get() + "]";
-    }
-}
diff --git a/core/java/android/view/autofill/FieldId.aidl b/core/java/android/view/autofill/FieldId.aidl
deleted file mode 100644
index 35af645..0000000
--- a/core/java/android/view/autofill/FieldId.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-parcelable FieldId;
\ No newline at end of file
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
new file mode 100644
index 0000000..0433a8f
--- /dev/null
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
+import android.view.autofill.IAutoFillManagerClient;
+
+/**
+ * Mediator between apps being auto-filled and auto-fill service implementations.
+ *
+ * {@hide}
+ */
+interface IAutoFillManager {
+    boolean addClient(in IAutoFillManagerClient client, int userId);
+    oneway void startSession(in IBinder activityToken, in IBinder appCallback,
+            in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId);
+    oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
+            in AutoFillValue value, int flags, int userId);
+    oneway void finishSession(in IBinder activityToken, int userId);
+    oneway void setAuthenticationResult(in Bundle data,
+            in IBinder activityToken, int userId);
+}
diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
similarity index 64%
rename from core/java/android/service/autofill/IAutoFillAppCallback.aidl
rename to core/java/android/view/autofill/IAutoFillManagerClient.aidl
index c2e72e8..45f363d 100644
--- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -14,34 +14,33 @@
  * limitations under the License.
  */
 
-package android.service.autofill;
+package android.view.autofill;
 
 import java.util.List;
 
 import android.content.Intent;
 import android.content.IntentSender;
-import android.view.autofill.Dataset;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
 
 /**
  * Object running in the application process and responsible for auto-filling it.
  *
  * @hide
  */
-// TODO(b/33197203): rename IAutoFillAppSession
-oneway interface IAutoFillAppCallback {
+oneway interface IAutoFillManagerClient {
+    /**
+     * Notifies the client when the auto-fill enabled state changed.
+     */
+    void setState(boolean enabled);
+
     /**
       * Auto-fills the activity with the contents of a dataset.
       */
-    void autoFill(in Dataset dataset);
+    void autoFill(in List<AutoFillId> ids, in List<AutoFillValue> values);
 
     /**
-      * Start an intent sender from the context of the filled app
+      * Authenticates a fill response or a data set.
       */
-    void startIntentSender(in IntentSender intent, in Intent fillInIntent);
-
-    /**
-      * Called by system_service to enable auto-fill in a session, after it was asynchronously
-      * started by the manager.
-      */
-    void enableSession();
+    void authenticate(in IntentSender intent, in Intent fillInIntent);
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index d4baa18..84c8f7a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -340,8 +340,7 @@
     }
 
     @Override
-    public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
-            boolean alwaysUseOption) {
+    public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter) {
         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
         mChooserListAdapter = (ChooserListAdapter) adapter;
         if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
@@ -367,6 +366,9 @@
 
     @Override
     public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
+        // Note that this is only safe because the Intent handled by the ChooserActivity is
+        // guaranteed to contain no extras unknown to the local ClassLoader. That is why this
+        // method can not be replaced in the ResolverActivity whole hog.
         return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE,
                 super.shouldAutoLaunchSingleChoice(target));
     }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 4014217..3c1a180 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -20,22 +20,11 @@
 import android.annotation.StringRes;
 import android.annotation.UiThread;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.VoiceInteractor.PickOptionRequest;
 import android.app.VoiceInteractor.PickOptionRequest.Option;
 import android.app.VoiceInteractor.Prompt;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.provider.MediaStore;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.widget.AbsListView;
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
-
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -50,16 +39,23 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
+import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.Button;
@@ -67,7 +63,9 @@
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
-
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.widget.ResolverDrawerLayout;
@@ -92,7 +90,6 @@
 
     protected ResolveListAdapter mAdapter;
     private boolean mSafeForwardingMode;
-    private boolean mAlwaysUseOption;
     private AbsListView mAdapterView;
     private Button mAlwaysButton;
     private Button mOnceButton;
@@ -108,13 +105,15 @@
     private CharSequence mTitle;
     private int mDefaultTitleResId;
 
+    // Whether or not this activity supports choosing a default handler for the intent.
+    private boolean mSupportsAlwaysUseOption;
     protected ResolverDrawerLayout mResolverDrawerLayout;
     protected PackageManager mPm;
     protected int mLaunchedFromUid;
 
     private static final String TAG = "ResolverActivity";
     private static final boolean DEBUG = false;
-    private Runnable mPostListBuildRunnable;
+    private Runnable mPostListReadyRunnable;
 
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -230,13 +229,14 @@
      */
     protected void onCreate(Bundle savedInstanceState, Intent intent,
             CharSequence title, Intent[] initialIntents,
-            List<ResolveInfo> rList, boolean alwaysUseOption) {
-        onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
+            List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
+        onCreate(savedInstanceState, intent, title, 0, initialIntents, rList,
+                supportsAlwaysUseOption);
     }
 
     protected void onCreate(Bundle savedInstanceState, Intent intent,
             CharSequence title, int defaultTitleRes, Intent[] initialIntents,
-            List<ResolveInfo> rList, boolean alwaysUseOption) {
+            List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
         setTheme(R.style.Theme_DeviceDefault_Resolver);
         super.onCreate(savedInstanceState);
 
@@ -262,7 +262,7 @@
         mPackageMonitor.register(this, getMainLooper(), false);
         mRegistered = true;
         mReferrerPackage = getReferrerPackageName();
-        mAlwaysUseOption = alwaysUseOption;
+        mSupportsAlwaysUseOption = supportsAlwaysUseOption;
 
         final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
         mIconDpi = am.getLauncherLargeIconDensity();
@@ -378,8 +378,11 @@
         final DisplayResolveInfo dri = mAdapter.getOtherProfile();
         if (dri != null) {
             mProfileView.setVisibility(View.VISIBLE);
-            final TextView text = (TextView) mProfileView.findViewById(R.id.profile_button);
-            text.setText(dri.getDisplayLabel());
+            View text = mProfileView.findViewById(R.id.profile_button);
+            if (!(text instanceof TextView)) {
+                text = mProfileView.findViewById(R.id.text1);
+            }
+            ((TextView) text).setText(dri.getDisplayLabel());
         } else {
             mProfileView.setVisibility(View.GONE);
         }
@@ -421,7 +424,7 @@
         final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
         // While there may already be a filtered item, we can only use it in the title if the list
         // is already sorted and all information relevant to it is already in the list.
-        final boolean named = mAdapter.getFilteredPosition() > 0;
+        final boolean named = mAdapter.getFilteredPosition() >= 0;
         if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
             return getString(defaultTitleRes);
         } else {
@@ -512,15 +515,16 @@
         if (!isChangingConfigurations() && mPickOptionRequest != null) {
             mPickOptionRequest.cancel();
         }
-        if (mPostListBuildRunnable != null) {
-            getMainThreadHandler().removeCallbacks(mPostListBuildRunnable);
+        if (mPostListReadyRunnable != null) {
+            getMainThreadHandler().removeCallbacks(mPostListReadyRunnable);
+            mPostListReadyRunnable = null;
         }
     }
 
     @Override
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
-        if (mAlwaysUseOption) {
+        if (mSupportsAlwaysUseOption) {
             final int checkedPos = mAdapterView.getCheckedItemPosition();
             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
             mLastSelected = checkedPos;
@@ -575,17 +579,18 @@
 
     public void onButtonClick(View v) {
         final int id = v.getId();
-        startSelected(mAlwaysUseOption ?
-                        mAdapterView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
+        startSelected(mAdapter.hasFilteredItem() ?
+                        mAdapter.getFilteredPosition():
+                        mAdapterView.getCheckedItemPosition(),
                 id == R.id.button_always,
-                mAlwaysUseOption);
+                !mAdapter.hasFilteredItem());
     }
 
-    public void startSelected(int which, boolean always, boolean filtered) {
+    public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
         if (isFinishing()) {
             return;
         }
-        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
+        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
         if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
             Toast.makeText(this, String.format(getResources().getString(
                     com.android.internal.R.string.activity_resolver_work_profiles_support),
@@ -594,15 +599,15 @@
             return;
         }
 
-        TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
+        TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered);
         if (target == null) {
             return;
         }
         if (onTargetSelected(target, always)) {
-            if (always && filtered) {
+            if (always && mSupportsAlwaysUseOption) {
                 MetricsLogger.action(
                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
-            } else if (filtered) {
+            } else if (mSupportsAlwaysUseOption) {
                 MetricsLogger.action(
                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
             } else {
@@ -627,7 +632,7 @@
         final ResolveInfo ri = target.getResolveInfo();
         final Intent intent = target != null ? target.getResolvedIntent() : null;
 
-        if (intent != null && (mAlwaysUseOption || mAdapter.hasFilteredItem())
+        if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem())
                 && mAdapter.mUnfilteredResolveList != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
@@ -714,7 +719,18 @@
 
             if (filter != null) {
                 final int N = mAdapter.mUnfilteredResolveList.size();
-                ComponentName[] set = new ComponentName[N];
+                ComponentName[] set;
+                // If we don't add back in the component for forwarding the intent to a managed
+                // profile, the preferred activity may not be updated correctly (as the set of
+                // components we tell it we knew about will have changed).
+                final boolean needToAddBackProfileForwardingComponent
+                        = mAdapter.mOtherProfile != null;
+                if (!needToAddBackProfileForwardingComponent) {
+                    set = new ComponentName[N];
+                } else {
+                    set = new ComponentName[N + 1];
+                }
+
                 int bestMatch = 0;
                 for (int i=0; i<N; i++) {
                     ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0);
@@ -722,6 +738,13 @@
                             r.activityInfo.name);
                     if (r.match > bestMatch) bestMatch = r.match;
                 }
+
+                if (needToAddBackProfileForwardingComponent) {
+                    set[N] = mAdapter.mOtherProfile.getResolvedComponentName();
+                    final int otherProfileMatch = mAdapter.mOtherProfile.getResolveInfo().match;
+                    if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch;
+                }
+
                 if (alwaysCheck) {
                     final int userId = getUserId();
                     final PackageManager pm = getPackageManager();
@@ -859,15 +882,15 @@
         // a more complicated UI that the current voice interaction flow is not able
         // to handle.
         mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
-                mLaunchedFromUid, mAlwaysUseOption && !isVoiceInteraction());
+                mLaunchedFromUid, mSupportsAlwaysUseOption && !isVoiceInteraction());
         boolean rebuildCompleted = mAdapter.rebuildList();
 
-        if (mAdapter.hasFilteredItem()) {
+        if (useLayoutWithDefault()) {
             mLayoutId = R.layout.resolver_list_with_default;
-            mAlwaysUseOption = false;
         } else {
             mLayoutId = getLayoutResource();
         }
+        setContentView(mLayoutId);
 
         int count = mAdapter.getUnfilteredCount();
 
@@ -887,22 +910,21 @@
             }
         }
 
-        setContentView(mLayoutId);
+
         mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
 
         if (count == 0 && mAdapter.mPlaceholderCount == 0) {
-            final TextView empty = (TextView) findViewById(R.id.empty);
-            empty.setVisibility(View.VISIBLE);
+            final TextView emptyView = (TextView) findViewById(R.id.empty);
+            emptyView.setVisibility(View.VISIBLE);
             mAdapterView.setVisibility(View.GONE);
         } else {
             mAdapterView.setVisibility(View.VISIBLE);
-            onPrepareAdapterView(mAdapterView, mAdapter, mAlwaysUseOption);
+            onPrepareAdapterView(mAdapterView, mAdapter);
         }
         return false;
     }
 
-    public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
-            boolean alwaysUseOption) {
+    public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter) {
         final boolean useHeader = adapter.hasFilteredItem();
         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
 
@@ -912,7 +934,7 @@
         adapterView.setOnItemClickListener(listener);
         adapterView.setOnItemLongClickListener(listener);
 
-        if (alwaysUseOption) {
+        if (mSupportsAlwaysUseOption) {
             listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
         }
 
@@ -969,16 +991,19 @@
     }
 
     public void resetAlwaysOrOnceButtonBar() {
-        if (mAlwaysUseOption || mAdapter.mLastChosen != null) {
+        if (mSupportsAlwaysUseOption) {
             final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
             if (buttonLayout != null) {
                 buttonLayout.setVisibility(View.VISIBLE);
                 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
                 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
+            } else {
+                Log.e(TAG, "Layout unexpectedly does not have a button bar");
             }
         }
 
-        if (mAdapter.getFilteredPosition() >= 0) {
+        if (useLayoutWithDefault()
+                && mAdapter.getFilteredPosition() != ListView.INVALID_POSITION) {
             setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
             mOnceButton.setEnabled(true);
             return;
@@ -992,6 +1017,10 @@
         }
     }
 
+    private boolean useLayoutWithDefault() {
+        return mSupportsAlwaysUseOption && mAdapter.hasFilteredItem();
+    }
+
     /**
      * Check a simple match for the component of two ResolveInfos.
      */
@@ -1293,6 +1322,7 @@
             mPlaceholderCount = count;
         }
 
+        @Nullable
         public DisplayResolveInfo getFilteredItem() {
             if (mFilterLastUsed && mLastChosenPosition >= 0) {
                 // Not using getItem since it offsets to dodge this position for the list
@@ -1336,14 +1366,10 @@
          */
         protected boolean rebuildList() {
             List<ResolvedComponentInfo> currentResolveList = null;
-            try {
-                mLastChosen = mResolverListController.getLastChosen();
-            } catch (RemoteException re) {
-                Log.d(TAG, "Error calling getLastChosenActivity\n" + re);
-            }
-
             // Clear the value of mOtherProfile from previous call.
             mOtherProfile = null;
+            mLastChosen = null;
+            mLastChosenPosition = -1;
             mDisplayList.clear();
             if (mBaseResolveList != null) {
                 currentResolveList = mUnfilteredResolveList = new ArrayList<>();
@@ -1366,6 +1392,30 @@
                     mUnfilteredResolveList = originalList;
                 }
             }
+
+            // So far we only support a single other profile at a time.
+            // The first one we see gets special treatment.
+            for (ResolvedComponentInfo info : currentResolveList) {
+                if (info.getResolveInfoAt(0).targetUserId != UserHandle.USER_CURRENT) {
+                    mOtherProfile = new DisplayResolveInfo(info.getIntentAt(0),
+                            info.getResolveInfoAt(0),
+                            info.getResolveInfoAt(0).loadLabel(mPm),
+                            info.getResolveInfoAt(0).loadLabel(mPm),
+                            getReplacementIntent(info.getResolveInfoAt(0).activityInfo,
+                                    info.getIntentAt(0)));
+                    currentResolveList.remove(info);
+                    break;
+                }
+            }
+
+            if (mOtherProfile == null) {
+                try {
+                    mLastChosen = mResolverListController.getLastChosen();
+                } catch (RemoteException re) {
+                    Log.d(TAG, "Error calling getLastChosenActivity\n" + re);
+                }
+            }
+
             int N;
             if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
                 // We only care about fixing the unfilteredList if the current resolve list and
@@ -1402,19 +1452,7 @@
                         }
                     };
                     sortingTask.execute(currentResolveList);
-                    if (mPostListBuildRunnable == null) {
-                        mPostListBuildRunnable = new Runnable() {
-                            @Override
-                            public void run() {
-                                setTitleAndIcon();
-                                resetAlwaysOrOnceButtonBar();
-                                onListRebuilt();
-                                disableLastChosenIfNeeded();
-                                mPostListBuildRunnable = null;
-                            }
-                        };
-                        getMainThreadHandler().post(mPostListBuildRunnable);
-                    }
+                    postListReadyRunnable();
                     return false;
                 } else {
                     processSortedList(currentResolveList);
@@ -1426,16 +1464,6 @@
             }
         }
 
-        private void disableLastChosenIfNeeded() {
-            // Layout doesn't handle both profile button and last chosen
-            // so disable last chosen if profile button is present.
-            if (mOtherProfile != null && mLastChosenPosition >= 0) {
-                mLastChosenPosition = -1;
-                mFilterLastUsed = false;
-            }
-        }
-
-
         private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
             int N;
             if (sortedComponents != null && (N = sortedComponents.size()) != 0) {
@@ -1503,10 +1531,27 @@
                 processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
             }
 
-            setTitleAndIcon();
-            resetAlwaysOrOnceButtonBar();
-            disableLastChosenIfNeeded();
-            onListRebuilt();
+            postListReadyRunnable();
+        }
+
+        /**
+         * Some necessary methods for creating the list are initiated in onCreate and will also
+         * determine the layout known. We therefore can't update the UI inline and post to the
+         * handler thread to update after the current task is finished.
+         */
+        private void postListReadyRunnable() {
+            if (mPostListReadyRunnable == null) {
+                mPostListReadyRunnable = new Runnable() {
+                    @Override
+                    public void run() {
+                        setTitleAndIcon();
+                        resetAlwaysOrOnceButtonBar();
+                        onListRebuilt();
+                        mPostListReadyRunnable = null;
+                    }
+                };
+                getMainThreadHandler().post(mPostListReadyRunnable);
+            }
         }
 
         public void onListRebuilt() {
@@ -1588,6 +1633,11 @@
         }
 
         private void updateLastChosenPosition(ResolveInfo info) {
+            // If another profile is present, ignore the last chosen entry.
+            if (mOtherProfile != null) {
+                mLastChosenPosition = -1;
+                return;
+            }
             if (mLastChosen != null
                     && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
                     && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
@@ -1595,12 +1645,10 @@
             }
         }
 
+        // We assume that at this point we've already filtered out the only intent for a different
+        // targetUserId which we're going to use.
         private void addResolveInfo(DisplayResolveInfo dri) {
-            if (dri.mResolveInfo.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) {
-                // So far we only support a single other profile at a time.
-                // The first one we see gets special treatment.
-                mOtherProfile = dri;
-            } else {
+            if (dri.mResolveInfo.targetUserId == UserHandle.USER_CURRENT) {
                 mDisplayList.add(dri);
             }
         }
@@ -1841,7 +1889,8 @@
 
             final int checkedPos = mAdapterView.getCheckedItemPosition();
             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
-            if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
+            if (!useLayoutWithDefault()
+                    && (!hasValidSelection || mLastSelected != checkedPos)) {
                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
                 mOnceButton.setEnabled(hasValidSelection);
                 if (hasValidSelection) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 84195b2..804bd29 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3022,10 +3022,14 @@
                     @Override
                     public void onSwipeCancelled(SwipeDismissLayout layout) {
                         WindowManager.LayoutParams newParams = getAttributes();
-                        newParams.x = 0;
-                        newParams.alpha = 1;
-                        setAttributes(newParams);
-                        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
+                        // Swipe changes only affect the x-translation and alpha, check to see if
+                        // those values have changed first before resetting them.
+                        if (newParams.x != 0 || newParams.alpha != 1) {
+                            newParams.x = 0;
+                            newParams.alpha = 1;
+                            setAttributes(newParams);
+                            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
+                        }
                     }
                 });
     }
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 0fe580a..b4890b7 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -303,17 +303,17 @@
             return color;
         }
 
-        double[] lab = new double[3];
-        ColorUtilsFromCompat.colorToLAB(findFg ? fg : bg, lab);
+        float[] hsl = new float[3];
+        ColorUtilsFromCompat.colorToHSL(findFg ? fg : bg, hsl);
 
-        double low = lab[0], high = 100;
-        final double a = lab[1], b = lab[2];
+        float low = hsl[2], high = 1;
         for (int i = 0; i < 15 && high - low > 0.00001; i++) {
-            final double l = (low + high) / 2;
+            final float l = (low + high) / 2;
+            hsl[2] = l;
             if (findFg) {
-                fg = ColorUtilsFromCompat.LABToColor(l, a, b);
+                fg = ColorUtilsFromCompat.HSLToColor(hsl);
             } else {
-                bg = ColorUtilsFromCompat.LABToColor(l, a, b);
+                bg = ColorUtilsFromCompat.HSLToColor(hsl);
             }
             if (ColorUtilsFromCompat.calculateContrast(fg, bg) > minRatio) {
                 high = l;
@@ -321,7 +321,7 @@
                 low = l;
             }
         }
-        return ColorUtilsFromCompat.LABToColor(high, a, b);
+        return findFg ? fg : bg;
     }
 
     public static int ensureTextContrastOnBlack(int color) {
@@ -764,6 +764,10 @@
             return amount < low ? low : (amount > high ? high : amount);
         }
 
+        private static float constrain(float amount, float low, float high) {
+            return amount < low ? low : (amount > high ? high : amount);
+        }
+
         private static double pivotXyzComponent(double component) {
             return component > XYZ_EPSILON
                     ? Math.pow(component, 1 / 3.0)
@@ -779,5 +783,139 @@
             return result;
         }
 
+        /**
+         * Convert HSL (hue-saturation-lightness) components to a RGB color.
+         * <ul>
+         * <li>hsl[0] is Hue [0 .. 360)</li>
+         * <li>hsl[1] is Saturation [0...1]</li>
+         * <li>hsl[2] is Lightness [0...1]</li>
+         * </ul>
+         * If hsv values are out of range, they are pinned.
+         *
+         * @param hsl 3-element array which holds the input HSL components
+         * @return the resulting RGB color
+         */
+        @ColorInt
+        public static int HSLToColor(@NonNull float[] hsl) {
+            final float h = hsl[0];
+            final float s = hsl[1];
+            final float l = hsl[2];
+
+            final float c = (1f - Math.abs(2 * l - 1f)) * s;
+            final float m = l - 0.5f * c;
+            final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
+
+            final int hueSegment = (int) h / 60;
+
+            int r = 0, g = 0, b = 0;
+
+            switch (hueSegment) {
+                case 0:
+                    r = Math.round(255 * (c + m));
+                    g = Math.round(255 * (x + m));
+                    b = Math.round(255 * m);
+                    break;
+                case 1:
+                    r = Math.round(255 * (x + m));
+                    g = Math.round(255 * (c + m));
+                    b = Math.round(255 * m);
+                    break;
+                case 2:
+                    r = Math.round(255 * m);
+                    g = Math.round(255 * (c + m));
+                    b = Math.round(255 * (x + m));
+                    break;
+                case 3:
+                    r = Math.round(255 * m);
+                    g = Math.round(255 * (x + m));
+                    b = Math.round(255 * (c + m));
+                    break;
+                case 4:
+                    r = Math.round(255 * (x + m));
+                    g = Math.round(255 * m);
+                    b = Math.round(255 * (c + m));
+                    break;
+                case 5:
+                case 6:
+                    r = Math.round(255 * (c + m));
+                    g = Math.round(255 * m);
+                    b = Math.round(255 * (x + m));
+                    break;
+            }
+
+            r = constrain(r, 0, 255);
+            g = constrain(g, 0, 255);
+            b = constrain(b, 0, 255);
+
+            return Color.rgb(r, g, b);
+        }
+
+        /**
+         * Convert the ARGB color to its HSL (hue-saturation-lightness) components.
+         * <ul>
+         * <li>outHsl[0] is Hue [0 .. 360)</li>
+         * <li>outHsl[1] is Saturation [0...1]</li>
+         * <li>outHsl[2] is Lightness [0...1]</li>
+         * </ul>
+         *
+         * @param color  the ARGB color to convert. The alpha component is ignored
+         * @param outHsl 3-element array which holds the resulting HSL components
+         */
+        public static void colorToHSL(@ColorInt int color, @NonNull float[] outHsl) {
+            RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), outHsl);
+        }
+
+        /**
+         * Convert RGB components to HSL (hue-saturation-lightness).
+         * <ul>
+         * <li>outHsl[0] is Hue [0 .. 360)</li>
+         * <li>outHsl[1] is Saturation [0...1]</li>
+         * <li>outHsl[2] is Lightness [0...1]</li>
+         * </ul>
+         *
+         * @param r      red component value [0..255]
+         * @param g      green component value [0..255]
+         * @param b      blue component value [0..255]
+         * @param outHsl 3-element array which holds the resulting HSL components
+         */
+        public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
+                @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+                @NonNull float[] outHsl) {
+            final float rf = r / 255f;
+            final float gf = g / 255f;
+            final float bf = b / 255f;
+
+            final float max = Math.max(rf, Math.max(gf, bf));
+            final float min = Math.min(rf, Math.min(gf, bf));
+            final float deltaMaxMin = max - min;
+
+            float h, s;
+            float l = (max + min) / 2f;
+
+            if (max == min) {
+                // Monochromatic
+                h = s = 0f;
+            } else {
+                if (max == rf) {
+                    h = ((gf - bf) / deltaMaxMin) % 6f;
+                } else if (max == gf) {
+                    h = ((bf - rf) / deltaMaxMin) + 2f;
+                } else {
+                    h = ((rf - gf) / deltaMaxMin) + 4f;
+                }
+
+                s = deltaMaxMin / (1f - Math.abs(2f * l - 1f));
+            }
+
+            h = (h * 60f) % 360f;
+            if (h < 0) {
+                h += 360f;
+            }
+
+            outHsl[0] = constrain(h, 0f, 360f);
+            outHsl[1] = constrain(s, 0f, 1f);
+            outHsl[2] = constrain(l, 0f, 1f);
+        }
+
     }
 }
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 261fa43..6d814bf 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -79,7 +79,6 @@
     private boolean mDismissed;
     private boolean mDiscardIntercept;
     private VelocityTracker mVelocityTracker;
-    private float mTranslationX;
     private boolean mBlockGesture = false;
     private boolean mActivityTranslucencyConverted = false;
 
@@ -166,8 +165,10 @@
             return super.onInterceptTouchEvent(ev);
         }
 
-        // offset because the view is translated during swipe
-        ev.offsetLocation(mTranslationX, 0);
+        // Offset because the view is translated during swipe, match X with raw X. Active touch
+        // coordinates are mostly used by the velocity tracker, so offset it to match the raw
+        // coordinates which is what is primarily used elsewhere.
+        ev.offsetLocation(ev.getRawX() - ev.getX(), 0);
 
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
@@ -232,8 +233,12 @@
         if (mVelocityTracker == null || !mDismissable) {
             return super.onTouchEvent(ev);
         }
-        // offset because the view is translated during swipe
-        ev.offsetLocation(mTranslationX, 0);
+
+        // Offset because the view is translated during swipe, match X with raw X. Active touch
+        // coordinates are mostly used by the velocity tracker, so offset it to match the raw
+        // coordinates which is what is primarily used elsewhere.
+        ev.offsetLocation(ev.getRawX() - ev.getX(), 0);
+
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_UP:
                 updateDismiss(ev);
@@ -266,7 +271,6 @@
     }
 
     private void setProgress(float deltaX) {
-        mTranslationX = deltaX;
         if (mProgressListener != null && deltaX >= 0)  {
             mProgressListener.onSwipeProgressChanged(
                     this, progressToAlpha(deltaX / getWidth()), deltaX);
@@ -300,7 +304,6 @@
             mVelocityTracker.recycle();
         }
         mVelocityTracker = null;
-        mTranslationX = 0;
         mDownX = 0;
         mLastX = Integer.MIN_VALUE;
         mDownY = 0;
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index c49287c..c261e41 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -27,6 +27,7 @@
 #include "Bitmap.h"
 #include "SkDrawFilter.h"
 #include "SkGraphics.h"
+#include "SkRegion.h"
 
 namespace android {
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index b2c8168..2cfaeeb 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -37,6 +37,7 @@
 #include "android_media_AudioErrors.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_DeviceCallback.h"
+#include "android_media_VolumeShaper.h"
 
 #include <cinttypes>
 
@@ -64,6 +65,7 @@
 static audio_track_fields_t      javaAudioTrackFields;
 static audio_attributes_fields_t javaAudioAttrFields;
 static PlaybackParams::fields_t gPlaybackParamsFields;
+static VolumeShaperHelper::fields_t gVolumeShaperFields;
 
 struct audiotrack_callback_cookie {
     jclass      audioTrack_class;
@@ -1178,6 +1180,50 @@
     return FCC_8;
 }
 
+// Pass through the arguments to the AudioFlinger track implementation.
+static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
+        jobject jconfig, jobject joperation) {
+    // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
+    const int VOLUME_SHAPER_INVALID_OPERATION = -38;
+
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        return (jint)VOLUME_SHAPER_INVALID_OPERATION;
+    }
+
+    sp<VolumeShaper::Configuration> configuration;
+    sp<VolumeShaper::Operation> operation;
+    if (jconfig != nullptr) {
+        configuration = VolumeShaperHelper::convertJobjectToConfiguration(
+                env, gVolumeShaperFields, jconfig);
+        ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
+    }
+    if (joperation != nullptr) {
+        operation = VolumeShaperHelper::convertJobjectToOperation(
+                env, gVolumeShaperFields, joperation);
+        ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
+    }
+    VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
+    if (status == INVALID_OPERATION) {
+        status = VOLUME_SHAPER_INVALID_OPERATION;
+    }
+    return (jint)status; // if status < 0 an error, else a VolumeShaper id
+}
+
+// Pass through the arguments to the AudioFlinger track implementation.
+static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
+        jint id) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        return (jobject)nullptr;
+    }
+
+    sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
+    if (state.get() == nullptr) {
+        return (jobject)nullptr;
+    }
+    return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
+}
 
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
@@ -1242,6 +1288,12 @@
     {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
     {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
     {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
+    {"native_applyVolumeShaper",
+            "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
+                                         (void *)android_media_AudioTrack_apply_volume_shaper},
+    {"native_getVolumeShaperState",
+            "(I)Landroid/media/VolumeShaper$State;",
+                                        (void *)android_media_AudioTrack_get_volume_shaper_state},
 };
 
 
@@ -1312,6 +1364,7 @@
     // initialize PlaybackParams field info
     gPlaybackParamsFields.init(env);
 
+    gVolumeShaperFields.init(env);
     return res;
 }
 
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 65c1590..d934870 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -66,10 +66,10 @@
 }
 
 static void android_view_HardwareLayer_setSurfaceTexture(JNIEnv* env, jobject clazz,
-        jlong layerUpdaterPtr, jobject surface, jboolean isAlreadyAttached) {
+        jlong layerUpdaterPtr, jobject surface) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
-    layer->setSurfaceTexture(surfaceTexture, !isAlreadyAttached);
+    layer->setSurfaceTexture(surfaceTexture);
 }
 
 static void android_view_HardwareLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
@@ -88,7 +88,7 @@
     { "nPrepare",                "(JIIZ)Z",    (void*) android_view_HardwareLayer_prepare },
     { "nSetLayerPaint",          "(JJ)V",      (void*) android_view_HardwareLayer_setLayerPaint },
     { "nSetTransform",           "(JJ)V",      (void*) android_view_HardwareLayer_setTransform },
-    { "nSetSurfaceTexture",      "(JLandroid/graphics/SurfaceTexture;Z)V",
+    { "nSetSurfaceTexture",      "(JLandroid/graphics/SurfaceTexture;)V",
             (void*) android_view_HardwareLayer_setSurfaceTexture },
     { "nUpdateSurfaceTexture",   "(J)V",       (void*) android_view_HardwareLayer_updateSurfaceTexture },
 };
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d8fc1d1..36bb821 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8507,6 +8507,12 @@
         <attr name="fontWeight" format="integer" />
     </declare-styleable>
 
+    <!-- Attributes that are read when parsing a <fontfamily> tag, -->
+    <declare-styleable name="FontFamily">
+        <attr name="fontProviderAuthority" format="string" />
+        <attr name="fontProviderQuery" format="string" />
+    </declare-styleable>
+
     <!-- @hide -->
     <declare-styleable name="RecyclerView">
         <attr name="layoutManager" format="string" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 54c392f..72a2e43 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2541,7 +2541,7 @@
 
     <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
          These values are in DPs and will be converted to pixel sizes internally. -->
-    <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">8x8</string>
+    <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
 
     <!-- Max default size [WIDTHxHEIGHT] on screen for picture-in-picture windows to fit inside.
          These values are in DPs and will be converted to pixel sizes internally. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 34659aa..78489eb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2794,6 +2794,8 @@
         <public name="canCaptureFingerprintGestures" />
         <public name="alphabeticModifiers" />
         <public name="numericModifiers" />
+        <public name="fontProviderAuthority" />
+        <public name="fontProviderQuery" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
@@ -2806,6 +2808,7 @@
     <public type="attr" name="primaryContentAlpha" />
     <public type="attr" name="secondaryContentAlpha" />
 
+
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 91ce7a4..7b8c229 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1357,6 +1357,9 @@
             </intent-filter>
         </service>
 
+        <service android:name="android.content.CrossUserContentService"
+                android:exported="true" />
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/res/font/samplexmldownloadedfont.xml b/core/tests/coretests/res/font/samplexmldownloadedfont.xml
new file mode 100644
index 0000000..35391bd
--- /dev/null
+++ b/core/tests/coretests/res/font/samplexmldownloadedfont.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android"
+        android:fontProviderAuthority="com.example.test.fontprovider"
+        android:fontProviderQuery="MyRequestedFont">
+</font-family>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/activity/LocalProvider.java b/core/tests/coretests/src/android/app/activity/LocalProvider.java
index d6a10c2..b7a63ce 100644
--- a/core/tests/coretests/src/android/app/activity/LocalProvider.java
+++ b/core/tests/coretests/src/android/app/activity/LocalProvider.java
@@ -29,6 +29,19 @@
 public class LocalProvider extends ContentProvider {
     private static final String TAG = "LocalProvider";
 
+    private static final String AUTHORITY = "com.android.frameworks.coretests.LocalProvider";
+    private static final String TABLE_DATA_NAME = "data";
+    public static final Uri TABLE_DATA_URI =
+            Uri.parse("content://" + AUTHORITY + "/" + TABLE_DATA_NAME);
+
+    public static final String COLUMN_TEXT_NAME = "text";
+    public static final String COLUMN_INTEGER_NAME = "integer";
+
+    public static final String TEXT1 = "first data";
+    public static final String TEXT2 = "second data";
+    public static final int INTEGER1 = 100;
+    public static final int INTEGER2 = 101;
+
     private SQLiteOpenHelper mOpenHelper;
 
     private static final int DATA = 1;
@@ -51,13 +64,20 @@
 
         @Override
         public void onCreate(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE data (" +
+            db.execSQL("CREATE TABLE " + TABLE_DATA_NAME + " (" +
                        "_id INTEGER PRIMARY KEY," +
-                       "text TEXT, " +
-                       "integer INTEGER);");
+                       COLUMN_TEXT_NAME + " TEXT, " +
+                       COLUMN_INTEGER_NAME + " INTEGER);");
 
             // insert alarms
-            db.execSQL("INSERT INTO data (text, integer) VALUES ('first data', 100);");
+            db.execSQL(getInsertCommand(TEXT1, INTEGER1));
+            db.execSQL(getInsertCommand(TEXT2, INTEGER2));
+        }
+
+        private String getInsertCommand(String textValue, int integerValue) {
+            return "INSERT INTO " + TABLE_DATA_NAME
+                    + " (" + COLUMN_TEXT_NAME + ", " + COLUMN_INTEGER_NAME + ") "
+                    + "VALUES ('" + textValue + "', " + integerValue + ");";
         }
 
         @Override
@@ -74,6 +94,10 @@
     public LocalProvider() {
     }
 
+    static public Uri getTableDataUriForRow(int rowId) {
+        return Uri.parse("content://" + AUTHORITY + "/" + TABLE_DATA_NAME + "/" + rowId);
+    }
+
     @Override
     public boolean onCreate() {
         mOpenHelper = new DatabaseHelper(getContext());
diff --git a/core/tests/coretests/src/android/content/CrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/CrossUserContentResolverTest.java
new file mode 100644
index 0000000..027ba8e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/CrossUserContentResolverTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.activity.LocalProvider;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class CrossUserContentResolverTest {
+    private final static int TIMEOUT_SERVICE_CONNECTION_SEC = 4;
+    private final static int TIMEOUT_CONTENT_CHANGE_SEC = 4;
+
+    private Context mContext;
+    private UserManager mUm;
+    private int mSecondaryUserId = -1;
+    private CrossUserContentServiceConnection mServiceConnection;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mUm = UserManager.get(mContext);
+        final UserInfo userInfo = mUm.createUser("Test user", 0);
+        mSecondaryUserId = userInfo.id;
+        final PackageManager pm = mContext.getPackageManager();
+        pm.installExistingPackageAsUser(mContext.getPackageName(), mSecondaryUserId);
+        ActivityManager.getService().startUserInBackground(mSecondaryUserId);
+
+        final CountDownLatch connectionLatch = new CountDownLatch(1);
+        mServiceConnection = new CrossUserContentServiceConnection(connectionLatch);
+        mContext.bindServiceAsUser(
+                new Intent(mContext, CrossUserContentService.class),
+                mServiceConnection,
+                Context.BIND_AUTO_CREATE,
+                UserHandle.of(mSecondaryUserId));
+        if (!connectionLatch.await(TIMEOUT_SERVICE_CONNECTION_SEC, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for service connection to establish");
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mSecondaryUserId != -1) {
+            mUm.removeUser(mSecondaryUserId);
+        }
+        if (mServiceConnection != null) {
+            mContext.unbindService(mServiceConnection);
+        }
+    }
+
+    /**
+     * Register an observer for an URI in the secondary user and verify that it receives
+     * onChange callback when data at the URI changes.
+     */
+    @Test
+    public void testRegisterContentObserver() throws Exception {
+        Context secondaryUserContext = null;
+        String packageName = null;
+        try {
+            packageName = InstrumentationRegistry.getContext().getPackageName();
+            secondaryUserContext =
+                    InstrumentationRegistry.getContext().createPackageContextAsUser(
+                            packageName, 0 /* flags */, UserHandle.of(mSecondaryUserId));
+        } catch (NameNotFoundException e) {
+            fail("Couldn't find package " + packageName + " in u" + mSecondaryUserId);
+        }
+
+        final CountDownLatch updateLatch = new CountDownLatch(1);
+        final Uri uriToUpdate = LocalProvider.getTableDataUriForRow(2);
+        final TestContentObserver observer = new TestContentObserver(updateLatch,
+                uriToUpdate, mSecondaryUserId);
+        secondaryUserContext.getContentResolver().registerContentObserver(
+                LocalProvider.TABLE_DATA_URI, true, observer, mSecondaryUserId);
+        mServiceConnection.getService().updateContent(uriToUpdate, "New Text", 42);
+        if (!updateLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for the content change callback");
+        }
+    }
+
+    /**
+     * Register an observer for an URI in the current user and verify that secondary user can
+     * notify changes for this URI.
+     */
+    @Test
+    public void testNotifyChange() throws Exception {
+        final CountDownLatch notifyLatch = new CountDownLatch(1);
+        final Uri notifyUri = LocalProvider.TABLE_DATA_URI;
+        final TestContentObserver observer = new TestContentObserver(notifyLatch,
+                notifyUri, UserHandle.myUserId());
+        mContext.getContentResolver().registerContentObserver(notifyUri, true, observer);
+        mServiceConnection.getService().notifyForUriAsUser(notifyUri, UserHandle.myUserId());
+        if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for the notify callback");
+        }
+    }
+
+    private static final class CrossUserContentServiceConnection implements ServiceConnection {
+        private ICrossUserContentService mService;
+        private final CountDownLatch mLatch;
+
+        public CrossUserContentServiceConnection(CountDownLatch latch) {
+            mLatch = latch;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = ICrossUserContentService.Stub.asInterface(service);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+        }
+
+        public ICrossUserContentService getService() {
+            return mService;
+        }
+    }
+
+    private static final class TestContentObserver extends ContentObserver {
+        private final CountDownLatch mLatch;
+        private final Uri mExpectedUri;
+        private final int mExpectedUserId;
+
+        public TestContentObserver(CountDownLatch latch, Uri exptectedUri, int expectedUserId) {
+            super(null);
+            mLatch = latch;
+            mExpectedUri = exptectedUri;
+            mExpectedUserId = expectedUserId;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (mExpectedUri.equals(uri) && mExpectedUserId == userId) {
+                mLatch.countDown();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/CrossUserContentService.java b/core/tests/coretests/src/android/content/CrossUserContentService.java
new file mode 100644
index 0000000..9cbe549
--- /dev/null
+++ b/core/tests/coretests/src/android/content/CrossUserContentService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content;
+
+import android.app.Service;
+import android.app.activity.LocalProvider;
+import android.net.Uri;
+import android.os.IBinder;
+
+public class CrossUserContentService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mLocalService.asBinder();
+    }
+
+    private ICrossUserContentService mLocalService = new ICrossUserContentService.Stub() {
+        @Override
+        public void updateContent(Uri uri, String key, int value) {
+            final ContentValues values = new ContentValues();
+            values.put(LocalProvider.COLUMN_TEXT_NAME, key);
+            values.put(LocalProvider.COLUMN_INTEGER_NAME, value);
+            getContentResolver().update(uri, values, null, null);
+        }
+
+        @Override
+        public void notifyForUriAsUser(Uri uri, int userId) {
+            getContentResolver().notifyChange(uri, null, false, userId);
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/ICrossUserContentService.aidl b/core/tests/coretests/src/android/content/ICrossUserContentService.aidl
new file mode 100644
index 0000000..2c5cde4
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ICrossUserContentService.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content;
+
+import android.net.Uri;
+
+interface ICrossUserContentService {
+    void updateContent(in Uri uri, String key, int value);
+    void notifyForUriAsUser(in Uri uri, int userId);
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 380a28774..8b536a7 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -15,6 +15,8 @@
  */
 package android.content.res;
 
+import static junit.framework.Assert.assertNull;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
@@ -78,4 +80,19 @@
         assertEquals(true, font4.isItalic());
         assertEquals("res/font/samplefont4.ttf", font4.getFontName());
     }
+
+    @Test
+    public void testParseDownloadableFont() throws IOException, XmlPullParserException {
+        XmlResourceParser parser = mResources.getXml(R.font.samplexmldownloadedfont);
+
+        FontConfig result = FontResourcesParser.parse(parser, mResources);
+
+        assertNotNull(result);
+        List<FontConfig.Family> families = result.getFamilies();
+        assertEquals(1, families.size());
+        FontConfig.Family family = families.get(0);
+        assertEquals("com.example.test.fontprovider", family.getProviderAuthority());
+        assertEquals("MyRequestedFont", family.getQuery());
+        assertNull(family.getFonts());
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index daebf88..1080a9f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -211,6 +211,119 @@
         assertThat(activity.isFinishing(), is(true));
     }
 
+    @Test
+    public void hasOtherProfileOneOption() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(2);
+        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        // The other entry is filtered to the other profile slot
+        assertThat(activity.getAdapter().getCount(), is(1));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        // Make a stable copy of the components as the original list may be modified
+        List<ResolvedComponentInfo> stableCopy =
+                createResolvedComponentsForTestWithOtherProfile(2);
+        // Check that the "Other Profile" activity is put in the right spot
+        onView(withId(R.id.profile_button)).check(matches(
+                withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    @Test
+    public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3);
+        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getLastChosen())
+                .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        // The other entry is filtered to the other profile slot
+        assertThat(activity.getAdapter().getCount(), is(2));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        // Make a stable copy of the components as the original list may be modified
+        List<ResolvedComponentInfo> stableCopy =
+                createResolvedComponentsForTestWithOtherProfile(3);
+        // Check that the "Other Profile" activity is put in the right spot
+        onView(withId(R.id.profile_button)).check(matches(
+                withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    @Test
+    public void hasLastChosenActivityAndOtherProfile() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3);
+        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        // The other entry is filtered to the last used slot
+        assertThat(activity.getAdapter().getCount(), is(2));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        ChooserWrapperActivity.sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        // Make a stable copy of the components as the original list may be modified
+        List<ResolvedComponentInfo> stableCopy =
+                createResolvedComponentsForTestWithOtherProfile(3);
+        // Check that the "Other Profile" activity is put in the right spot
+        onView(withId(R.id.profile_button)).check(matches(
+                withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
     private Intent createSendImageIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -227,6 +340,19 @@
         return infoList;
     }
 
+    private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
+            int numberOfResults) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            if (i == 0) {
+                infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i));
+            } else {
+                infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+            }
+        }
+        return infoList;
+    }
+
     private void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 84b844a..2c23018 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -43,6 +43,7 @@
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
@@ -102,6 +103,7 @@
     public void hasLastChosenActivity() throws Exception {
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
 
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
@@ -121,14 +123,133 @@
             return true;
         };
 
-        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
-        onView(withId(R.id.title)).perform(click());
+        onView(withId(R.id.button_once)).perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    @Test
+    public void hasOtherProfileOneOption() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(2);
+        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        // The other entry is filtered to the last used slot
+        assertThat(activity.getAdapter().getCount(), is(1));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        // Make a stable copy of the components as the original list may be modified
+        List<ResolvedComponentInfo> stableCopy =
+                createResolvedComponentsForTestWithOtherProfile(2);
+        // Check that the "Other Profile" activity is put in the right spot
+        onView(withId(R.id.profile_button)).check(matches(
+                withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                .perform(click());
         onView(withId(R.id.button_once))
                 .perform(click());
         waitForIdle();
         assertThat(chosen[0], is(toChoose));
     }
 
+    @Test
+    public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3);
+        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        // The other entry is filtered to the other profile slot
+        assertThat(activity.getAdapter().getCount(), is(2));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        // Confirm that the button bar is disabled by default
+        onView(withId(R.id.button_once)).check(matches(not(isEnabled())));
+
+        // Make a stable copy of the components as the original list may be modified
+        List<ResolvedComponentInfo> stableCopy =
+                createResolvedComponentsForTestWithOtherProfile(2);
+
+        // Check that the "Other Profile" activity is put in the right spot
+        onView(withId(R.id.profile_button)).check(matches(
+                withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                .perform(click());
+        onView(withId(R.id.button_once)).perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+
+    @Test
+    public void hasLastChosenActivityAndOtherProfile() throws Exception {
+        // In this case we prefer the other profile and don't display anything about the last
+        // chosen activity.
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3);
+        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        when(sOverrides.resolverListController.getLastChosen())
+                .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        // The other entry is filtered to the other profile slot
+        assertThat(activity.getAdapter().getCount(), is(2));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        // Confirm that the button bar is disabled by default
+        onView(withId(R.id.button_once)).check(matches(not(isEnabled())));
+
+        // Make a stable copy of the components as the original list may be modified
+        List<ResolvedComponentInfo> stableCopy =
+                createResolvedComponentsForTestWithOtherProfile(2);
+
+        // Check that the "Other Profile" activity is put in the right spot
+        onView(withId(R.id.profile_button)).check(matches(
+                withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                .perform(click());
+        onView(withId(R.id.button_once)).perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
     private Intent createSendImageIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -145,6 +266,19 @@
         return infoList;
     }
 
+    private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
+            int numberOfResults) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            if (i == 0) {
+                infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i));
+            } else {
+                infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+            }
+        }
+        return infoList;
+    }
+
     private void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index ae06306..c710b9a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -32,9 +32,16 @@
  */
 class ResolverDataProvider {
 
+    static private int USER_SOMEONE_ELSE = 10;
+
     static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
         return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
-                createResolverIntent(i), createResolveInfo(i));
+                createResolverIntent(i), createResolveInfo(i, UserHandle.USER_CURRENT));
+    }
+
+    static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i) {
+        return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
+                createResolverIntent(i), createResolveInfo(i, USER_SOMEONE_ELSE));
     }
 
     static ComponentName createComponentName(int i) {
@@ -42,10 +49,10 @@
         return new ComponentName("foo.bar." + name, name);
     }
 
-    static ResolveInfo createResolveInfo(int i) {
+    static ResolveInfo createResolveInfo(int i, int userId) {
         final ResolveInfo resolveInfo = new ResolveInfo();
         resolveInfo.activityInfo = createActivityInfo(i);
-        resolveInfo.targetUserId = UserHandle.USER_CURRENT;
+        resolveInfo.targetUserId = userId;
         return resolveInfo;
     }
 
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
new file mode 100644
index 0000000..c1e8c98
--- /dev/null
+++ b/core/tests/packagemanagertests/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    frameworks-base-testutils
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/packagemanagertests/AndroidManifest.xml b/core/tests/packagemanagertests/AndroidManifest.xml
new file mode 100644
index 0000000..8f49008
--- /dev/null
+++ b/core/tests/packagemanagertests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          android:installLocation="internalOnly"
+          package="com.android.frameworks.coretests.packagemanager"
+          android:sharedUserId="android.uid.system">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.frameworks.coretests.packagemanager"
+            android:label="Frameworks PackageManager Core Tests" />
+
+</manifest>
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
new file mode 100644
index 0000000..1097bc7
--- /dev/null
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This test needs to be run without any secondary users on the device,
+ * and selinux needs to be disabled with "adb shell setenforce 0".
+ */
+@RunWith(AndroidJUnit4.class)
+public class KernelPackageMappingTests {
+
+    private static final String TAG = "KernelPackageMapping";
+    private static final String SDCARDFS_PATH = "/config/sdcardfs";
+
+    private UserInfo mSecondaryUser;
+
+    private static File getKernelPackageDir(String packageName) {
+        return new File(new File(SDCARDFS_PATH), packageName);
+    }
+
+    private static File getKernelPackageFile(String packageName, String filename) {
+        return new File(getKernelPackageDir(packageName), filename);
+    }
+
+    private UserManager getUserManager() {
+        UserManager um = (UserManager) InstrumentationRegistry.getContext().getSystemService(
+                Context.USER_SERVICE);
+        return um;
+    }
+
+    private IPackageManager getIPackageManager() {
+        IPackageManager ipm = IPackageManager.Stub.asInterface(
+                ServiceManager.getService("package"));
+        return ipm;
+    }
+
+    private static String getContent(File file) {
+        try {
+            return FileUtils.readTextFile(file, 0, null).trim();
+        } catch (IOException ioe) {
+            Log.w(TAG, "Couldn't read file " + file.getAbsolutePath() + "\n" + ioe);
+            return "<error>";
+        }
+    }
+
+    @Test
+    public void testInstalledPrimary() throws Exception {
+        assertEquals("1000", getContent(getKernelPackageFile("com.android.settings", "appid")));
+    }
+
+    @Test
+    public void testInstalledAll() throws Exception {
+        assertEquals("", getContent(getKernelPackageFile("com.android.settings",
+                "excluded_userids")));
+    }
+
+    @Test
+    public void testNotInstalledSecondary() throws Exception {
+        mSecondaryUser = getUserManager().createUser("Secondary", 0);
+        assertEquals(Integer.toString(mSecondaryUser.id),
+                getContent(
+                        getKernelPackageFile("com.android.frameworks.coretests.packagemanager",
+                                "excluded_userids")));
+    }
+
+    @After
+    public void shutDown() throws Exception {
+        if (mSecondaryUser != null) {
+            getUserManager().removeUser(mSecondaryUser.id);
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 23e7305..7289429 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.os.Build;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -290,11 +291,6 @@
     /** @hide */
     @IntDef(flag = true,
             value = {
-                MATRIX_SAVE_FLAG,
-                CLIP_SAVE_FLAG,
-                HAS_ALPHA_LAYER_SAVE_FLAG,
-                FULL_COLOR_LAYER_SAVE_FLAG,
-                CLIP_TO_LAYER_SAVE_FLAG,
                 ALL_SAVE_FLAG
             })
     @Retention(RetentionPolicy.SOURCE)
@@ -302,21 +298,39 @@
 
     /**
      * Restore the current matrix when restore() is called.
+     *
+     * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
+     *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the matrix
+     *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
+     *             canvases and as of API level {@value Build.VERSION_CODES#O} that is the default
+     *             behavior for all canvas types.
      */
     public static final int MATRIX_SAVE_FLAG = 0x01;
 
     /**
      * Restore the current clip when restore() is called.
+     *
+     * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
+     *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the clip
+     *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
+     *             canvases and as of API level {@value Build.VERSION_CODES#O} that is the default
+     *             behavior for all canvas types.
      */
     public static final int CLIP_SAVE_FLAG = 0x02;
 
     /**
      * The layer requires a per-pixel alpha channel.
+     *
+     * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
+     *             {@link #saveLayerAlpha(RectF, int)}.
      */
     public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04;
 
     /**
      * The layer requires full 8-bit precision for each color channel.
+     *
+     * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
+     *             {@link #saveLayerAlpha(RectF, int)}.
      */
     public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08;
 
@@ -326,6 +340,10 @@
      * omit this flag for any call to <code>saveLayer()</code> and
      * <code>saveLayerAlpha()</code> variants. Not passing this flag generally
      * triggers extremely poor performance with hardware accelerated rendering.
+     *
+     * @deprecated This flag results in poor performance and the same effect can be achieved with
+     *             a single layer or multiple draw commands with different clips.
+     *
      */
     public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
 
@@ -335,6 +353,9 @@
      * strongly recommended to pass this - the complete set of flags - to any
      * call to <code>saveLayer()</code> and <code>saveLayerAlpha()</code>
      * variants.
+     *
+     * <p class="note"><strong>Note:</strong> all methods that accept this flag
+     * have flagless versions that are equivalent to passing this flag.
      */
     public static final int ALL_SAVE_FLAG = 0x1F;
 
@@ -364,6 +385,7 @@
      * restore() is made, those calls will be forgotten, and the settings that
      * existed before the save() will be reinstated.
      *
+     * @deprecated Use {@link #save()} instead.
      * @param saveFlags flag bits that specify which parts of the Canvas state
      *                  to save/restore
      * @return The value to pass to restoreToCount() to balance this save()
@@ -394,6 +416,7 @@
      * {@link Paint#getColorFilter() ColorFilter} are applied when the
      * offscreen bitmap is drawn back when restore() is called.
      *
+     * @deprecated Use {@link #saveLayer(RectF, Paint)} instead.
      * @param bounds May be null. The maximum size the offscreen bitmap
      *               needs to be (in local coordinates)
      * @param paint  This is copied, and is applied to the offscreen when
@@ -410,7 +433,30 @@
     }
 
     /**
-     * Convenience for saveLayer(bounds, paint, {@link #ALL_SAVE_FLAG})
+     * This behaves the same as save(), but in addition it allocates and
+     * redirects drawing to an offscreen rendering target.
+     * <p class="note"><strong>Note:</strong> this method is very expensive,
+     * incurring more than double rendering cost for contained content. Avoid
+     * using this method when possible and instead use a
+     * {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
+     * to apply an xfermode, color filter, or alpha, as it will perform much
+     * better than this method.
+     * <p>
+     * All drawing calls are directed to a newly allocated offscreen rendering target.
+     * Only when the balancing call to restore() is made, is that offscreen
+     * buffer drawn back to the current target of the Canvas (which can potentially be a previous
+     * layer if these calls are nested).
+     * <p>
+     * Attributes of the Paint - {@link Paint#getAlpha() alpha},
+     * {@link Paint#getXfermode() Xfermode}, and
+     * {@link Paint#getColorFilter() ColorFilter} are applied when the
+     * offscreen rendering target is drawn back when restore() is called.
+     *
+     * @param bounds May be null. The maximum size the offscreen render target
+     *               needs to be (in local coordinates)
+     * @param paint  This is copied, and is applied to the offscreen when
+     *               restore() is called.
+     * @return       value to pass to restoreToCount() to balance this save()
      */
     public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint) {
         return saveLayer(bounds, paint, ALL_SAVE_FLAG);
@@ -418,6 +464,8 @@
 
     /**
      * Helper version of saveLayer() that takes 4 values rather than a RectF.
+     *
+     * @deprecated Use {@link #saveLayer(float, float, float, float, Paint)} instead.
      */
     public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
             @Saveflags int saveFlags) {
@@ -427,7 +475,8 @@
     }
 
     /**
-     * Convenience for saveLayer(left, top, right, bottom, paint, {@link #ALL_SAVE_FLAG})
+     * Convenience for {@link #saveLayer(RectF, Paint)} that takes the four float coordinates of the
+     * bounds rectangle.
      */
     public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint) {
         return saveLayer(left, top, right, bottom, paint, ALL_SAVE_FLAG);
@@ -453,6 +502,7 @@
      * The {@code alpha} parameter is applied when the offscreen bitmap is
      * drawn back when restore() is called.
      *
+     * @deprecated Use {@link #saveLayerAlpha(RectF, int)} instead.
      * @param bounds    The maximum size the offscreen bitmap needs to be
      *                  (in local coordinates)
      * @param alpha     The alpha to apply to the offscreen when it is
@@ -469,7 +519,13 @@
     }
 
     /**
-     * Convenience for saveLayerAlpha(bounds, alpha, {@link #ALL_SAVE_FLAG})
+     * Convenience for {@link #saveLayer(RectF, Paint)} but instead of taking a entire Paint object
+     * it takes only the {@code alpha} parameter.
+     *
+     * @param bounds    The maximum size the offscreen bitmap needs to be
+     *                  (in local coordinates)
+     * @param alpha     The alpha to apply to the offscreen when it is
+                        drawn during restore()
      */
     public int saveLayerAlpha(@Nullable RectF bounds, int alpha) {
         return saveLayerAlpha(bounds, alpha, ALL_SAVE_FLAG);
@@ -477,6 +533,8 @@
 
     /**
      * Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
+     *
+     * @deprecated Use {@link #saveLayerAlpha(float, float, float, float, int)} instead.
      */
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             @Saveflags int saveFlags) {
@@ -486,7 +544,8 @@
     }
 
     /**
-     * Helper for saveLayerAlpha(left, top, right, bottom, alpha, {@link #ALL_SAVE_FLAG})
+     * Convenience for {@link #saveLayerAlpha(RectF, int)} that takes the four float coordinates of
+     * the bounds rectangle.
      */
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) {
         return saveLayerAlpha(left, top, right, bottom, alpha, ALL_SAVE_FLAG);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 750ef3f..7eb8099 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -163,36 +163,54 @@
     @Nullable
     public static Typeface createFromResources(FontConfig config, AssetManager mgr, String path) {
         if (sFallbackFonts != null) {
+            Typeface typeface = findFromCache(mgr, path);
+            if (typeface != null) return typeface;
+
+            List<FontConfig.Family> families = config.getFamilies();
+            if (families == null || families.isEmpty()) {
+                throw new RuntimeException(
+                        "Font resource " + path + " contained no font families.");
+            }
+            if (families.size() > 1) {
+                throw new RuntimeException(
+                        "Font resource " + path + " contained more than one family.");
+            }
+            FontConfig.Family family = families.get(0);
+            if (family.getProviderAuthority() != null && family.getQuery() != null) {
+                // Downloadable font
+                typeface = findFromCache(
+                        family.getProviderAuthority(), family.getQuery());
+                if (typeface != null) {
+                    return typeface;
+                }
+                // Downloaded font and it wasn't cached, request it again and return a
+                // default font instead (nothing we can do now).
+                create(new FontRequest(family.getProviderAuthority(), family.getQuery()),
+                        NO_OP_REQUEST_CALLBACK);
+                return DEFAULT;
+            }
+
+            FontFamily fontFamily = new FontFamily();
+            List<FontConfig.Font> fonts = family.getFonts();
+            if (fonts == null || fonts.isEmpty()) {
+                throw new RuntimeException("Font resource " + path + " contained no fonts.");
+            }
+            for (int i = 0; i < fonts.size(); i++) {
+                FontConfig.Font font = fonts.get(i);
+                // TODO: Use style and weight info
+                if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(),
+                        0 /* resourceCookie */, false /* isAsset */)) {
+                    return null;
+                }
+            }
+            fontFamily.freeze();
+            FontFamily[] familyChain = { fontFamily };
+            typeface = createFromFamiliesWithDefault(familyChain);
             synchronized (sDynamicTypefaceCache) {
                 final String key = createAssetUid(mgr, path);
-                Typeface typeface = sDynamicTypefaceCache.get(key);
-                if (typeface != null) return typeface;
-
-                List<FontConfig.Family> families = config.getFamilies();
-                if (families == null || families.isEmpty()) {
-                    throw new RuntimeException("Font resource contained no fonts.");
-                }
-                if (families.size() > 1) {
-                    throw new RuntimeException("Font resource contained more than one family.");
-                }
-                FontConfig.Family family = families.get(0);
-
-                FontFamily fontFamily = new FontFamily();
-                List<FontConfig.Font> fonts = family.getFonts();
-                for (int i = 0; i < fonts.size(); i++) {
-                    FontConfig.Font font = fonts.get(i);
-                    // TODO: Use style and weight info
-                    if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(),
-                            0 /* resourceCookie */, false /* isAsset */)) {
-                        return null;
-                    }
-                }
-                fontFamily.freeze();
-                FontFamily[] familyChain = { fontFamily };
-                typeface = createFromFamiliesWithDefault(familyChain);
                 sDynamicTypefaceCache.put(key, typeface);
-                return typeface;
             }
+            return typeface;
         }
         return null;
     }
@@ -372,6 +390,18 @@
         void onTypefaceRequestFailed(@FontRequestFailReason int reason);
     }
 
+    private static final FontRequestCallback NO_OP_REQUEST_CALLBACK = new FontRequestCallback() {
+        @Override
+        public void onTypefaceRetrieved(Typeface typeface) {
+            // Do nothing.
+        }
+
+        @Override
+        public void onTypefaceRequestFailed(@FontRequestFailReason int reason) {
+            // Do nothing.
+        }
+    };
+
     /**
      * Create a typeface object given a family name, and option style information.
      * If null is passed for the name, then the "default" font will be chosen.
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 9772009..60c3b1c 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -67,6 +67,8 @@
     public static final int TYPE_DATA     = 3;
     /** @hide */
     public static final int TYPE_URI      = 4;
+    /** @hide */
+    public static final int TYPE_BITMAP_MASKABLE      = 5;
 
     private static final int VERSION_STREAM_SERIALIZER = 1;
 
@@ -101,6 +103,7 @@
      * {@link #TYPE_RESOURCE},
      * {@link #TYPE_DATA}, or
      * {@link #TYPE_URI}.
+     * {@link #TYPE_BITMAP_MASKABLE}
      * @hide
      */
     public int getType() {
@@ -112,7 +115,7 @@
      * @hide
      */
     public Bitmap getBitmap() {
-        if (mType != TYPE_BITMAP) {
+        if (mType != TYPE_BITMAP && mType != TYPE_BITMAP_MASKABLE) {
             throw new IllegalStateException("called getBitmap() on " + this);
         }
         return (Bitmap) mObj1;
@@ -218,6 +221,7 @@
     private static final String typeToString(int x) {
         switch (x) {
             case TYPE_BITMAP: return "BITMAP";
+            case TYPE_BITMAP_MASKABLE: return "BITMAP_MASKABLE";
             case TYPE_DATA: return "DATA";
             case TYPE_RESOURCE: return "RESOURCE";
             case TYPE_URI: return "URI";
@@ -285,6 +289,9 @@
         switch (mType) {
             case TYPE_BITMAP:
                 return new BitmapDrawable(context.getResources(), getBitmap());
+            case TYPE_BITMAP_MASKABLE:
+                return new MaskableIconDrawable(null,
+                    new BitmapDrawable(context.getResources(), getBitmap()));
             case TYPE_RESOURCE:
                 if (getResources() == null) {
                     // figure out where to load resources from
@@ -388,7 +395,7 @@
      * @hide
      */
     public void convertToAshmem() {
-        if (mType == TYPE_BITMAP &&
+        if ((mType == TYPE_BITMAP || mType == TYPE_BITMAP_MASKABLE) &&
             getBitmap().isMutable() &&
             getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) {
             setBitmap(getBitmap().createAshmemBitmap());
@@ -409,6 +416,7 @@
 
         switch (mType) {
             case TYPE_BITMAP:
+            case TYPE_BITMAP_MASKABLE:
                 getBitmap().compress(Bitmap.CompressFormat.PNG, 100, dataStream);
                 break;
             case TYPE_DATA:
@@ -444,6 +452,8 @@
             switch (type) {
                 case TYPE_BITMAP:
                     return createWithBitmap(BitmapFactory.decodeStream(inputStream));
+                case TYPE_BITMAP_MASKABLE:
+                    return createWithMaskableBitmap(BitmapFactory.decodeStream(inputStream));
                 case TYPE_DATA:
                     final int length = inputStream.readInt();
                     final byte[] data = new byte[length];
@@ -478,6 +488,7 @@
         }
         switch (mType) {
             case TYPE_BITMAP:
+            case TYPE_BITMAP_MASKABLE:
                 return getBitmap() == otherIcon.getBitmap();
             case TYPE_DATA:
                 return getDataLength() == otherIcon.getDataLength()
@@ -551,6 +562,20 @@
     }
 
     /**
+     * Create an Icon pointing to a bitmap in memory that follows the icon design guideline defined
+     * by {@link MaskableIconDrawable}.
+     * @param bits A valid {@link android.graphics.Bitmap} object
+     */
+    public static Icon createWithMaskableBitmap(Bitmap bits) {
+        if (bits == null) {
+            throw new IllegalArgumentException("Bitmap must not be null.");
+        }
+        final Icon rep = new Icon(TYPE_BITMAP_MASKABLE);
+        rep.setBitmap(bits);
+        return rep;
+    }
+
+    /**
      * Create an Icon pointing to a compressed bitmap stored in a byte array.
      * @param data Byte array storing compressed bitmap data of a type that
      *             {@link android.graphics.BitmapFactory}
@@ -654,6 +679,7 @@
         final StringBuilder sb = new StringBuilder("Icon(typ=").append(typeToString(mType));
         switch (mType) {
             case TYPE_BITMAP:
+            case TYPE_BITMAP_MASKABLE:
                 sb.append(" size=")
                         .append(getBitmap().getWidth())
                         .append("x")
@@ -692,7 +718,7 @@
      * Parcelable interface
      */
     public int describeContents() {
-        return (mType == TYPE_BITMAP || mType == TYPE_DATA)
+        return (mType == TYPE_BITMAP || mType == TYPE_BITMAP_MASKABLE || mType == TYPE_DATA)
                 ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
     }
 
@@ -702,6 +728,7 @@
         this(in.readInt());
         switch (mType) {
             case TYPE_BITMAP:
+            case TYPE_BITMAP_MASKABLE:
                 final Bitmap bits = Bitmap.CREATOR.createFromParcel(in);
                 mObj1 = bits;
                 break;
@@ -740,6 +767,7 @@
         dest.writeInt(mType);
         switch (mType) {
             case TYPE_BITMAP:
+            case TYPE_BITMAP_MASKABLE:
                 final Bitmap bits = getBitmap();
                 getBitmap().writeToParcel(dest, flags);
                 break;
diff --git a/graphics/java/android/graphics/drawable/MaskableIconDrawable.java b/graphics/java/android/graphics/drawable/MaskableIconDrawable.java
index 043f092..e4f1788a 100644
--- a/graphics/java/android/graphics/drawable/MaskableIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/MaskableIconDrawable.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -62,17 +63,22 @@
 
     /**
      * Mask path is defined inside device configuration in following dimension: [100 x 100]
+     * @hide
      */
     public static final float MASK_SIZE = 100f;
+    private static final float SAFEZONE_SCALE = .9f;
 
     /**
-     * The view port of the layers is smaller than their intrinsic width and height by this factor.
-     *
-     * It is part of the API contract that all four sides of the layers are padded so as to provide
+     * All four sides of the layers are padded with extra inset so as to provide
      * extra content to reveal within the clip path when performing affine transformations on the
      * layers.
+     *
+     * Each layers will reserve 25% of it's width and height.
+     *
+     * As a result, the view port of the layers is smaller than their intrinsic width and height.
      */
-    public static final float DEFAULT_VIEW_PORT_SCALE = 2f / 3f;
+    private static final float EXTRA_INSET_PERCENTAGE = 1 / 4f;
+    private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
 
     /**
      * Clip path defined in {@link com.android.internal.R.string.config_icon_mask}.
@@ -155,12 +161,17 @@
      *
      * @param backgroundDrawable drawable that should be rendered in the background
      * @param foregroundDrawable drawable that should be rendered in the foreground
+     * @hide
      */
     public MaskableIconDrawable(Drawable backgroundDrawable,
             Drawable foregroundDrawable) {
         this((LayerState)null, null);
-        addLayer(BACKGROUND_ID, createChildDrawable(backgroundDrawable));
-        addLayer(FOREGROUND_ID, createChildDrawable(foregroundDrawable));
+        if (backgroundDrawable != null) {
+            addLayer(BACKGROUND_ID, createChildDrawable(backgroundDrawable));
+        }
+        if (foregroundDrawable != null) {
+            addLayer(FOREGROUND_ID, createChildDrawable(foregroundDrawable));
+        }
     }
 
     /**
@@ -199,6 +210,15 @@
     }
 
     /**
+     * All four sides of the layers are padded with extra inset so as to provide
+     * extra content to reveal within the clip path when performing affine transformations on the
+     * layers.
+     */
+    public static float getExtraInsetPercentage() {
+        return EXTRA_INSET_PERCENTAGE;
+    }
+
+    /**
      * @return the mask path object used to clip the drawable
      */
     public Path getIconMask() {
@@ -242,13 +262,20 @@
         int cY = bounds.centerY();
 
         for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
+            final ChildDrawable r = mLayerState.mChildren[i];
+            if (r == null) {
+                continue;
+            }
+            final Drawable d = r.mDrawable;
+            if (d == null) {
+                continue;
+            }
+
             int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
             int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
             final Rect outRect = mTmpOutRect;
             outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
 
-            final ChildDrawable r = mLayerState.mChildren[i];
-            final Drawable d = r.mDrawable;
             d.setBounds(outRect);
         }
     }
@@ -273,6 +300,9 @@
         if (mLayersShader == null) {
             mCanvas.setBitmap(mLayersBitmap);
             for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+                if (mLayerState.mChildren[i] == null) {
+                    continue;
+                }
                 final Drawable dr = mLayerState.mChildren[i].mDrawable;
                 if (dr != null) {
                     dr.draw(mCanvas);
@@ -295,6 +325,18 @@
         outline.setConvexPath(mMask);
     }
 
+    /** @hide */
+    @TestApi
+    public Region getSafeZone() {
+        mMaskMatrix.reset();
+        mMaskMatrix.setScale(SAFEZONE_SCALE, SAFEZONE_SCALE, getBounds().centerX(), getBounds().centerY());
+        Path p = new Path();
+        mMask.transform(mMaskMatrix, p);
+        Region safezoneRegion = new Region(getBounds());
+        safezoneRegion.setPath(p, safezoneRegion);
+        return safezoneRegion;
+    }
+
     @Override
     public @Nullable Region getTransparentRegion() {
         if (mTransparentRegion.isEmpty()) {
diff --git a/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java b/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java
index a214b9e..50c498b 100644
--- a/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java
+++ b/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Region;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Parcel;
@@ -108,6 +109,40 @@
     }
 
     @SmallTest
+    public void testWithMaskableBitmap() throws Exception {
+        final Bitmap bm1 = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
+
+        final Canvas can1 = new Canvas(bm1);
+        can1.drawColor(0xFFFF0000);
+
+        final Icon im1 = Icon.createWithMaskableBitmap(bm1);
+
+        final MaskableIconDrawable draw1 = (MaskableIconDrawable) im1.loadDrawable(mContext);
+
+        final Bitmap test1 = Bitmap.createBitmap(
+            (int)(draw1.getIntrinsicWidth() * (1 + 2 * MaskableIconDrawable.getExtraInsetPercentage())),
+            (int)(draw1.getIntrinsicHeight() * (1 + 2 * MaskableIconDrawable.getExtraInsetPercentage())),
+            Bitmap.Config.ARGB_8888);
+
+        draw1.setBounds(0, 0,
+            (int) (draw1.getIntrinsicWidth() * (1 + 2 * MaskableIconDrawable.getExtraInsetPercentage())),
+            (int) (draw1.getIntrinsicHeight() * (1 + 2 * MaskableIconDrawable.getExtraInsetPercentage())));
+        draw1.draw(new Canvas(test1));
+
+        final File dir = getContext().getExternalFilesDir(null);
+        L("writing temp bitmaps to %s...", dir);
+
+        bm1.compress(Bitmap.CompressFormat.PNG, 100,
+            new FileOutputStream(new File(dir, "maskable-bitmap1-original.png")));
+        test1.compress(Bitmap.CompressFormat.PNG, 100,
+            new FileOutputStream(new File(dir, "maskable-bitmap1-test.png")));
+        if (!equalBitmaps(bm1, test1, draw1.getSafeZone())) {
+            findBitmapDifferences(bm1, test1);
+            fail("maskable bitmap1 differs, check " + dir);
+        }
+    }
+
+    @SmallTest
     public void testWithBitmapResource() throws Exception {
         final Bitmap res1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape))
                 .getBitmap();
@@ -294,17 +329,31 @@
         printBits(aPix, w, h);
     }
     boolean equalBitmaps(Bitmap a, Bitmap b) {
+        return equalBitmaps(a, b, null);
+    }
+
+    boolean equalBitmaps(Bitmap a, Bitmap b, Region region) {
         if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false;
-        
+
         final int w = a.getWidth();
         final int h = a.getHeight();
         int[] aPix = new int[w * h];
         int[] bPix = new int[w * h];
 
-        a.getPixels(aPix, 0, w, 0, 0, w, h);
-        b.getPixels(bPix, 0, w, 0, 0, w, h);
-
-        return Arrays.equals(aPix, bPix);
+        if (region != null) {
+            for (int i = 0; i < w; i++) {
+                for (int j = 0; j < h; j++) {
+                    if (region.contains(i, j) && a.getPixel(i, j) != b.getPixel(i, j)) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        } else {
+            a.getPixels(aPix, 0, w, 0, 0, w, h);
+            b.getPixels(bPix, 0, w, 0, 0, w, h);
+            return Arrays.equals(aPix, bPix);
+        }
     }
 
     void findBitmapDifferences(Bitmap a, Bitmap b) {
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index e566b9d..9981668 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -588,7 +588,7 @@
      * @hide for reuse by CertInstaller and Settings.
      * @see KeyChain#bind
      */
-    public final static class KeyChainConnection implements Closeable {
+    public static class KeyChainConnection implements Closeable {
         private final Context context;
         private final ServiceConnection serviceConnection;
         private final IKeyChainService service;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 9701b0e..988e32c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -200,6 +200,11 @@
                 }
 
                 if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
+                    if (mKeySizeBits < 64) {
+                        throw new InvalidAlgorithmParameterException(
+                            "HMAC key size must be at least 64 bits.");
+                    }
+
                     // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
                     // implies SHA-256 digest). Because keymaster HMAC key is authorized only for
                     // one digest, we don't let algorithm parameter spec override the digest implied
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 415e850..00e8c05 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -31,7 +31,7 @@
         , mBlend(false)
         , mSurfaceTexture(nullptr)
         , mTransform(nullptr)
-        , mNeedsGLContextAttach(false)
+        , mGLContextAttached(false)
         , mUpdateTexImage(false)
         , mLayer(nullptr)
         , mLayerApi(layerApi)
@@ -47,10 +47,21 @@
 }
 
 void DeferredLayerUpdater::destroyLayer() {
-    if (mLayer) {
-        mLayer->postDecStrong();
-        mLayer = nullptr;
+    if (!mLayer) {
+        return;
     }
+
+    if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) {
+        status_t err = mSurfaceTexture->detachFromContext();
+        mGLContextAttached = false;
+        if (err != 0) {
+            // TODO: Elevate to fatal exception
+            ALOGE("Failed to detach SurfaceTexture from context %d", err);
+        }
+    }
+
+    mLayer->postDecStrong();
+    mLayer = nullptr;
 }
 
 void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
@@ -78,8 +89,9 @@
             LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
                                 "apply surfaceTexture with non GL backend %x, GL %x, VK %x",
                                 mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
-            if (mNeedsGLContextAttach) {
-                mNeedsGLContextAttach = false;
+            if (!mGLContextAttached) {
+                mGLContextAttached = true;
+                mUpdateTexImage = true;
                 mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId());
             }
             if (mUpdateTexImage) {
@@ -169,16 +181,7 @@
 
 void DeferredLayerUpdater::detachSurfaceTexture() {
     if (mSurfaceTexture.get()) {
-        if (mLayerApi == Layer::Api::OpenGL) {
-            status_t err = mSurfaceTexture->detachFromContext();
-            if (err != 0) {
-                // TODO: Elevate to fatal exception
-                ALOGE("Failed to detach SurfaceTexture from context %d", err);
-            }
-            if (mLayer) {
-                static_cast<GlLayer*>(mLayer)->clearTexture();
-            }
-        }
+        destroyLayer();
         mSurfaceTexture = nullptr;
     }
 }
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 064b724..6717361 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -68,9 +68,8 @@
         return false;
     }
 
-    ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture, bool needsAttach) {
+    ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture) {
         if (texture.get() != mSurfaceTexture.get()) {
-            mNeedsGLContextAttach = needsAttach;
             mSurfaceTexture = texture;
 
             GLenum target = texture->getCurrentTextureTarget();
@@ -122,7 +121,7 @@
     SkBlendMode mMode = SkBlendMode::kSrcOver;
     sp<GLConsumer> mSurfaceTexture;
     SkMatrix* mTransform;
-    bool mNeedsGLContextAttach;
+    bool mGLContextAttached;
     bool mUpdateTexImage;
 
     Layer* mLayer;
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
index 8174bcc..aacad54 100644
--- a/libs/hwui/GlLayer.cpp
+++ b/libs/hwui/GlLayer.cpp
@@ -43,7 +43,10 @@
 }
 
 GlLayer::~GlLayer() {
-    if (texture.mId) {
+    // There's a rare possibility that Caches could have been destroyed already
+    // since this method is queued up as a task.
+    // Since this is a reset method, treat this as non-fatal.
+    if (caches.isInitialized() && texture.mId) {
         texture.deleteTexture();
     }
 }
@@ -64,15 +67,5 @@
     }
 }
 
-void GlLayer::clearTexture() {
-    // There's a rare possibility that Caches could have been destroyed already
-    // since this method is queued up as a task.
-    // Since this is a reset method, treat this as non-fatal.
-    if (caches.isInitialized()) {
-        caches.textureState().unbindTexture(texture.mId);
-    }
-    texture.mId = 0;
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
index 23dfd9d..85ddaff 100644
--- a/libs/hwui/GlLayer.h
+++ b/libs/hwui/GlLayer.h
@@ -88,13 +88,6 @@
     void generateTexture();
 
     /**
-     * When the caller frees the texture itself, the caller
-     * must call this method to tell this layer that it lost
-     * the texture.
-     */
-    void clearTexture();
-
-    /**
      * Lost the GL context but the layer is still around, mark it invalid internally
      * so the dtor knows not to do any GL work
      */
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index e54bc36..c57b1b3 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -198,9 +198,7 @@
 int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
             const SkPaint* paint, SaveFlags::Flags flags) {
     const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    //always save matrix and clip to match the behaviour of Skia and HWUI pipelines and to ensure
-    //android state tracking behavior matches that of the Skia API (partial save is not supported)
-    const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags | SaveFlags::MatrixClip));
+    const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags));
 
     return mCanvas->saveLayer(rec);
 }
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index e03c9e8..a7ebb68 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -48,21 +48,17 @@
         SkBitmap bitmap;
         SkPaint paint;
         hwuiBitmap->getSkBitmapForShaders(&bitmap);
-
-        sk_sp<SkShader> repeatShader = SkMakeBitmapShader(bitmap,
+        sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+        sk_sp<SkShader> repeatShader = image->makeShader(
                 SkShader::TileMode::kRepeat_TileMode,
                 SkShader::TileMode::kRepeat_TileMode,
-                nullptr,
-                kNever_SkCopyPixelsMode,
                 nullptr);
         paint.setShader(std::move(repeatShader));
         canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint);
 
-        sk_sp<SkShader> mirrorShader = SkMakeBitmapShader(bitmap,
+        sk_sp<SkShader> mirrorShader = image->makeShader(
                 SkShader::TileMode::kMirror_TileMode,
                 SkShader::TileMode::kMirror_TileMode,
-                nullptr,
-                kNever_SkCopyPixelsMode,
                 nullptr);
         paint.setShader(std::move(mirrorShader));
         canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint);
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 124f5fa..669f03c 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -748,11 +748,10 @@
         SkPaint paint;
         SkBitmap skBitmap;
         bitmap->getSkBitmap(&skBitmap);
-        sk_sp<SkShader> shader = SkMakeBitmapShader(skBitmap,
+        sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+        sk_sp<SkShader> shader = image->makeShader(
                 SkShader::TileMode::kClamp_TileMode,
                 SkShader::TileMode::kClamp_TileMode,
-                nullptr,
-                kNever_SkCopyPixelsMode,
                 nullptr);
         paint.setShader(std::move(shader));
         canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
@@ -767,11 +766,10 @@
         SkPaint paint;
         SkBitmap skBitmap;
         bitmap->getSkBitmap(&skBitmap);
-        sk_sp<SkShader> shader1 = SkMakeBitmapShader(skBitmap,
+        sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+        sk_sp<SkShader> shader1 = image->makeShader(
                 SkShader::TileMode::kClamp_TileMode,
                 SkShader::TileMode::kClamp_TileMode,
-                nullptr,
-                kNever_SkCopyPixelsMode,
                 nullptr);
 
         SkPoint center;
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 03e6b7f..7ae58a6 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -39,12 +39,10 @@
  */
 TEST(SkiaBehavior, CreateBitmapShader1x1) {
     SkBitmap origBitmap = createSkBitmap(1, 1);
-    sk_sp<SkShader> s = SkMakeBitmapShader(
-            origBitmap,
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(origBitmap, kNever_SkCopyPixelsMode);
+    sk_sp<SkShader> s = image->makeShader(
             SkShader::kClamp_TileMode,
             SkShader::kRepeat_TileMode,
-            nullptr,
-            kNever_SkCopyPixelsMode,
             nullptr);
 
     SkBitmap bitmap;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 9083c16..ddd8a65 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1741,6 +1741,17 @@
         return setStereoVolume(gain, gain);
     }
 
+    @Override
+    /* package */ int playerApplyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation) {
+        return native_applyVolumeShaper(configuration, operation);
+    }
+
+    @Override
+    /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
+        return native_getVolumeShaperState(id);
+    }
 
     /**
      * Sets the playback sample rate for this track. This sets the sampling rate at which
@@ -3093,6 +3104,12 @@
     private native final void native_disableDeviceCallback();
     static private native int native_get_FCC_8();
 
+    private native int native_applyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation);
+
+    private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/IPlayer.aidl b/media/java/android/media/IPlayer.aidl
index f068a0a..2d60bf9 100644
--- a/media/java/android/media/IPlayer.aidl
+++ b/media/java/android/media/IPlayer.aidl
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.media.VolumeShaper;
 
 /**
  * @hide
@@ -27,4 +28,6 @@
     oneway void setVolume(float vol);
     oneway void setPan(float pan);
     oneway void setStartDelayMs(int delayMs);
+    oneway void applyVolumeShaper(in VolumeShaper.Configuration configuration,
+                                  in VolumeShaper.Operation operation);
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1a1d0f3..c5a47ec 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1331,6 +1331,24 @@
         stop();
     }
 
+    @Override
+    /* package */ int playerApplyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation) {
+        return native_applyVolumeShaper(configuration, operation);
+    }
+
+    @Override
+    /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
+        return native_getVolumeShaperState(id);
+    }
+
+    private native int native_applyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation);
+
+    private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+
     /**
      * Set the low-level power management behavior for this MediaPlayer.  This
      * can be used when the MediaPlayer is not playing through a SurfaceHolder
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index d2b052a..b397b45 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -17,9 +17,11 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.media.VolumeShaper;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -357,6 +359,42 @@
      * @param rightVolume the right volume to use if muting is false
      */
     abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);
+
+    /**
+     * Abstract method to apply a {@link VolumeShaper.Configuration}
+     * and a {@link VolumeShaper.Operation} to the Player.
+     * This should be overridden by the Player to call into the native
+     * VolumeShaper implementation. Multiple {@code VolumeShapers} may be
+     * concurrently active for a given Player, each accessible by the
+     * {@code VolumeShaper} id.
+     *
+     * The {@code VolumeShaper} implementation caches the id returned
+     * when applying a fully specified configuration
+     * from {VolumeShaper.Configuration.Builder} to track later
+     * operation changes requested on it.
+     *
+     * @param configuration a {@code VolumeShaper.Configuration} object
+     *        created by {@link VolumeShaper.Configuration.Builder} or
+     *        an created from a {@code VolumeShaper} id
+     *        by the {@link VolumeShaper.Configuration} constructor.
+     * @param operation a {@code VolumeShaper.Operation}.
+     * @return a negative error status or a
+     *         non-negative {@code VolumeShaper} id on success.
+     */
+    /* package */ abstract int playerApplyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation);
+
+    /**
+     * Abstract method to get the current VolumeShaper state.
+     * @param id the {@code VolumeShaper} id returned from
+     *           sending a fully specified {@code VolumeShaper.Configuration}
+     *           through {@link #playerApplyVolumeShaper}
+     * @return a {@code VolumeShaper.State} object or null if
+     *         there is no {@code VolumeShaper} for the id.
+     */
+    /* package */ abstract @Nullable VolumeShaper.State playerGetVolumeShaperState(int id);
+
     abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
     abstract void playerStart();
     abstract void playerPause();
@@ -396,6 +434,13 @@
         public void setStartDelayMs(int delayMs) {
             baseSetStartDelayMs(delayMs);
         }
+
+        @Override
+        public void applyVolumeShaper(
+                @NonNull VolumeShaper.Configuration configuration,
+                @NonNull VolumeShaper.Operation operation) {
+            /* void */ playerApplyVolumeShaper(configuration, operation);
+        }
     };
 
     //=====================================================================
diff --git a/media/java/android/media/PlayerProxy.java b/media/java/android/media/PlayerProxy.java
index 1a2c668..5f3997a 100644
--- a/media/java/android/media/PlayerProxy.java
+++ b/media/java/android/media/PlayerProxy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.media.VolumeShaper;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -132,4 +133,21 @@
         }
     }
 
+    /**
+     * @hide
+     * @param configuration
+     * @param operation
+     * @return volume shaper id or error
+     */
+    public void applyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation) {
+        try {
+            mConf.getIPlayer().applyVolumeShaper(configuration, operation);
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for applyVolumeShaper operation,"
+                    + " player already released?", e);
+        }
+    }
 }
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 4cc1f8e..dbbbfc6 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -20,6 +20,8 @@
 import java.io.FileDescriptor;
 import java.lang.ref.WeakReference;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -388,6 +390,17 @@
         _setVolume(streamID, leftVolume, rightVolume);
     }
 
+    @Override
+    /* package */ int playerApplyVolumeShaper(
+            @NonNull VolumeShaper.Configuration configuration,
+            @Nullable VolumeShaper.Operation operation) {
+        return -1;
+    }
+
+    @Override
+    /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
+        return null;
+    }
 
     @Override
     void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
diff --git a/core/java/android/view/autofill/FillResponse.aidl b/media/java/android/media/VolumeShaper.aidl
similarity index 68%
copy from core/java/android/view/autofill/FillResponse.aidl
copy to media/java/android/media/VolumeShaper.aidl
index b018f15..ecf6a8f 100644
--- a/core/java/android/view/autofill/FillResponse.aidl
+++ b/media/java/android/media/VolumeShaper.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright 2017 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
+ *      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,
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
-package android.view.autofill;
+package android.media;
 
-parcelable FillResponse;
\ No newline at end of file
+parcelable VolumeShaper.Configuration;
+parcelable VolumeShaper.Operation;
+parcelable VolumeShaper.State;
\ No newline at end of file
diff --git a/media/java/android/media/VolumeShaper.java b/media/java/android/media/VolumeShaper.java
new file mode 100644
index 0000000..77af359
--- /dev/null
+++ b/media/java/android/media/VolumeShaper.java
@@ -0,0 +1,1275 @@
+/*
+ * Copyright 2017 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.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+
+/**
+ * TODO: remove @hide
+ * The {@code VolumeShaper} class is used to automatically control audio volume during media
+ * playback, allowing for simple implementation of transition effects and ducking.
+ *
+ * The {@link VolumeShaper} appears as an additional scaling on the audio output,
+ * and can be used independently of track or stream volume controls.
+ */
+public final class VolumeShaper {
+    /* member variables */
+    private int mId;
+    private final WeakReference<PlayerBase> mPlayerBase;
+    private final WeakReference<PlayerProxy> mPlayerProxy;
+
+    /**
+     * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and an
+     * {@link AudioTrack}.
+     *
+     * @param configuration
+     * @param audioTrack
+     */
+    public VolumeShaper(@NonNull Configuration configuration, @NonNull AudioTrack audioTrack) {
+        this(configuration, (PlayerBase)audioTrack);
+    }
+
+    /**
+     * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
+     * {@link MediaPlayer}.
+     *
+     * @param configuration
+     * @param mediaPlayer
+     */
+    public VolumeShaper(@NonNull Configuration configuration, @NonNull MediaPlayer mediaPlayer) {
+        this(configuration, (PlayerBase)mediaPlayer);
+    }
+
+    /* package */ VolumeShaper(
+            @NonNull Configuration configuration, @NonNull PlayerBase playerBase) {
+        mPlayerBase = new WeakReference<PlayerBase>(playerBase);
+        mPlayerProxy = null;
+        mId = applyPlayer(configuration, new Operation.Builder().defer().build());
+    }
+
+    /**
+     * @hide
+     * TODO SystemApi
+     * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
+     * {@code PlayerProxy} object.  The PlayerProxy object requires that the configuration
+     * be set with a system VolumeShaper id (this is a reserved value).
+     *
+     * @param configuration
+     * @param playerProxy
+     */
+    public VolumeShaper(
+            @NonNull Configuration configuration, @NonNull PlayerProxy playerProxy) {
+        if (configuration.getId() < 0) {
+            throw new IllegalArgumentException("playerProxy configuration id must be specified");
+        }
+        mPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
+        mPlayerBase = null;
+        mId = applyPlayer(configuration, new Operation.Builder().defer().build());
+    }
+
+    /* package */ int getId() {
+        return mId;
+    }
+
+    /**
+     * Applies the {@link VolumeShaper.Operation} to the {@code VolumeShaper}.
+     * @param operation
+     */
+    public void apply(@NonNull Operation operation) {
+        /* void */ applyPlayer(new VolumeShaper.Configuration(mId), operation);
+    }
+
+    /**
+     * Replaces the current {@code VolumeShaper}
+     * configuration with a new configuration.
+     *
+     * This can be used to dynamically change the {@code VolumeShaper}
+     * configuration by joining several
+     * {@code VolumeShaper} configurations together.
+     * This is useful if the user changes the volume while the
+     * {@code VolumeShaper} is in effect.
+     *
+     * @param configuration
+     * @param operation
+     * @param join
+     */
+    public void replace(
+            @NonNull Configuration configuration, @NonNull Operation operation, boolean join) {
+        mId = applyPlayer(
+                configuration,
+                new Operation.Builder(operation).replace(mId, join).build());
+    }
+
+    /**
+     * Returns the current volume scale attributable to the {@code VolumeShaper}.
+     *
+     * @return the volume, linearly represented as a value between 0.f and 1.f.
+     */
+    public float getVolume() {
+        return getStatePlayer(mId).getVolume();
+    }
+
+    /**
+     * Releases the {@code VolumeShaper}. Any volume scale due to the
+     * {@code VolumeShaper} is removed.
+     */
+    public void release() {
+        try {
+            /* void */ applyPlayer(
+                    new VolumeShaper.Configuration(mId),
+                    new Operation.Builder().terminate().build());
+        } catch (IllegalStateException ise) {
+            ; // ok
+        }
+        if (mPlayerBase != null) {
+            mPlayerBase.clear();
+        }
+        if (mPlayerProxy != null) {
+            mPlayerProxy.clear();
+        }
+    }
+
+    @Override
+    protected void finalize() {
+        release(); // ensure we remove the native volume shaper
+    }
+
+    /**
+     * Internal call to apply the configuration and operation to the Player.
+     * Returns a valid shaper id or throws the appropriate exception.
+     * @param configuration
+     * @param operation
+     * @return id a non-negative shaper id.
+     */
+    private int applyPlayer(
+            @NonNull VolumeShaper.Configuration configuration,
+            @NonNull VolumeShaper.Operation operation) {
+        final int id;
+        if (mPlayerProxy != null) {
+            // The PlayerProxy accepts only one way transactions so
+            // the Configuration must have an id set to one of the system
+            // ids (a positive value less than 16).
+            PlayerProxy player = mPlayerProxy.get();
+            if (player == null) {
+                throw new IllegalStateException("player deallocated");
+            }
+            id = configuration.getId();
+            if (id < 0) {
+                throw new IllegalArgumentException("proxy requires configuration with id");
+            }
+            player.applyVolumeShaper(configuration, operation);
+        } else if (mPlayerBase != null) {
+            PlayerBase player = mPlayerBase.get();
+            if (player == null) {
+                throw new IllegalStateException("player deallocated");
+            }
+            id = player.playerApplyVolumeShaper(configuration, operation);
+        } else {
+            throw new IllegalStateException("uninitialized shaper");
+        }
+        if (id < 0) {
+            // TODO - get INVALID_OPERATION from platform.
+            final int VOLUME_SHAPER_INVALID_OPERATION = -38; // must match with platform
+            // Due to RPC handling, we translate integer codes to exceptions right before
+            // delivering to the user.
+            if (id == VOLUME_SHAPER_INVALID_OPERATION) {
+                throw new IllegalStateException("player or volume shaper deallocated");
+            } else {
+                throw new IllegalArgumentException("invalid configuration or operation: " + id);
+            }
+        }
+        return id;
+    }
+
+    /**
+     * Internal call to retrieve the current VolumeShaper state.
+     * @param id
+     * @return the current {@vode VolumeShaper.State}
+     */
+    private @NonNull VolumeShaper.State getStatePlayer(int id) {
+        final VolumeShaper.State state;
+        if (mPlayerProxy != null) {
+            PlayerProxy player = mPlayerProxy.get();
+            if (player == null) {
+                throw new IllegalStateException("player deallocated");
+            }
+            throw new IllegalStateException("getStatePlayer not permitted through proxy");
+        } else if (mPlayerBase != null) {
+            PlayerBase player = mPlayerBase.get();
+            if (player == null) {
+                throw new IllegalStateException("player deallocated");
+            }
+            state = player.playerGetVolumeShaperState(id);
+        } else {
+            throw new IllegalStateException("uninitialized shaper");
+        }
+        if (state == null) {
+            throw new IllegalStateException("shaper cannot be found");
+        }
+        return state;
+    }
+
+    /**
+     * The {@code VolumeShaper.Configuration} class contains curve shape
+     * and parameter information for constructing a {@code VolumeShaper}.
+     * This curve shape and parameter information is specified
+     * on {@code VolumeShaper} creation
+     * and may be replaced through {@link VolumeShaper#replace}.
+     */
+    public static final class Configuration implements Parcelable {
+        private static final int MAXIMUM_CURVE_POINTS = 16;
+
+        /**
+         * Returns the maximum number of curve points allowed for
+         * {@link VolumeShaper.Builder#setCurve(float[], float[])}.
+         */
+        public static int getMaximumCurvePoints() {
+            return MAXIMUM_CURVE_POINTS;
+        }
+
+        // These values must match the native VolumeShaper::Configuration::Type
+        /** @hide */
+        @IntDef({
+            TYPE_ID,
+            TYPE_SCALE,
+            })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Type {}
+
+        /**
+         * Specifies a {@link VolumeShaper} handle created by {@link #VolumeShaper(int)}
+         * from an id returned by {@code setVolumeShaper()}.
+         * The type, curve, etc. may not be queried from
+         * a {@code VolumeShaper} object of this type;
+         * the handle is used to identify and change the operation of
+         * an existing {@code VolumeShaper} sent to the player.
+         */
+        /* package */ static final int TYPE_ID = 0;
+
+        /**
+         * Specifies a {@link VolumeShaper} to be used
+         * as an additional scale to the current volume.
+         * This is created by the {@link VolumeShaper.Builder}.
+         */
+        /* package */ static final int TYPE_SCALE = 1;
+
+        // These values must match the native InterpolatorType enumeration.
+        /** @hide */
+        @IntDef({
+            INTERPOLATOR_TYPE_STEP,
+            INTERPOLATOR_TYPE_LINEAR,
+            INTERPOLATOR_TYPE_CUBIC,
+            INTERPOLATOR_TYPE_CUBIC_MONOTONIC,
+            })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface InterpolatorType {}
+
+        /**
+         * Stepwise volume curve.
+         */
+        public static final int INTERPOLATOR_TYPE_STEP = 0;
+
+        /**
+         * Linear interpolated volume curve.
+         */
+        public static final int INTERPOLATOR_TYPE_LINEAR = 1;
+
+        /**
+         * Cubic interpolated volume curve.
+         * This is default if unspecified.
+         */
+        public static final int INTERPOLATOR_TYPE_CUBIC = 2;
+
+        /**
+         * Cubic interpolated volume curve
+         * with local monotonicity preservation.
+         * So long as the control points are locally monotonic,
+         * the curve interpolation will also be locally monotonic.
+         * This is useful for cubic spline interpolated
+         * volume ramps and ducks.
+         */
+        public static final int INTERPOLATOR_TYPE_CUBIC_MONOTONIC = 3;
+
+        // These values must match the native VolumeShaper::Configuration::InterpolatorType
+        /** @hide */
+        @IntDef({
+            OPTION_FLAG_VOLUME_IN_DBFS,
+            OPTION_FLAG_CLOCK_TIME,
+            })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface OptionFlag {}
+
+        /**
+         * Use a dB full scale volume range for the volume curve.
+         *<p>
+         * The volume scale is typically from 0.f to 1.f on a linear scale;
+         * this option changes to -inf to 0.f on a db full scale,
+         * where 0.f is equivalent to a scale of 1.f.
+         */
+        public static final int OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0);
+
+        /**
+         * Use clock time instead of media time.
+         *<p>
+         * The default implementation of {@code VolumeShaper} is to apply
+         * volume changes by the media time of the player.
+         * Hence, the {@code VolumeShaper} will speed or slow down to
+         * match player changes of playback rate, pause, or resume.
+         *<p>
+         * The {@code OPTION_FLAG_CLOCK_TIME} option allows the {@code VolumeShaper}
+         * progress to be determined by clock time instead of media time.
+         */
+        public static final int OPTION_FLAG_CLOCK_TIME = (1 << 1);
+
+        private static final int OPTION_FLAG_PUBLIC_ALL =
+                OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME;
+
+        /**
+         * A one second linear ramp from silence to full volume.
+         * Use {@link VolumeShaper.Builder#reflectTimes()} to generate
+         * the matching linear duck.
+         */
+        public static final Configuration LINEAR_RAMP = new VolumeShaper.Configuration.Builder()
+                .setInterpolatorType(INTERPOLATOR_TYPE_LINEAR)
+                .setCurve(new float[] {0.f, 1.f} /* times */,
+                        new float[] {0.f, 1.f} /* volumes */)
+                .setDurationMs(1000.)
+                .build();
+
+        /**
+         * A one second cubic ramp from silence to full volume.
+         * Use {@link VolumeShaper.Builder#reflectTimes()} to generate
+         * the matching cubic duck.
+         */
+        public static final Configuration CUBIC_RAMP = new VolumeShaper.Configuration.Builder()
+                .setInterpolatorType(INTERPOLATOR_TYPE_CUBIC)
+                .setCurve(new float[] {0.f, 1.f} /* times */,
+                        new float[] {0.f, 1.f}  /* volumes */)
+                .setDurationMs(1000.)
+                .build();
+
+        /**
+         * A one second sine curve for energy preserving cross fades.
+         * Use {@link VolumeShaper.Builder#reflectTimes()} to generate
+         * the matching cosine duck.
+         */
+        public static final Configuration SINE_RAMP;
+
+        /**
+         * A one second sine-squared s-curve ramp.
+         * Use {@link VolumeShaper.Builder#reflectTimes()}
+         * or {@link VolumeShaper.Builder#invertVolumes()} to generate
+         * the matching s-curve duck.
+         */
+        public static final Configuration SCURVE_RAMP;
+
+        static {
+            final int POINTS = MAXIMUM_CURVE_POINTS;
+            final float times[] = new float[POINTS];
+            final float sines[] = new float[POINTS];
+            final float scurve[] = new float[POINTS];
+            for (int i = 0; i < POINTS; ++i) {
+                times[i] = (float)i / (POINTS - 1);
+                final float sine = (float)Math.sin(times[i] * Math.PI / 2.);
+                sines[i] = sine;
+                scurve[i] = sine * sine;
+            }
+            SINE_RAMP = new VolumeShaper.Configuration.Builder()
+                .setInterpolatorType(INTERPOLATOR_TYPE_CUBIC)
+                .setCurve(times, sines)
+                .setDurationMs(1000.)
+                .build();
+            SCURVE_RAMP = new VolumeShaper.Configuration.Builder()
+                .setInterpolatorType(INTERPOLATOR_TYPE_CUBIC)
+                .setCurve(times, scurve)
+                .setDurationMs(1000.)
+                .build();
+        }
+
+        /*
+         * member variables - these are all final
+         */
+
+        // type of VolumeShaper
+        private final int mType;
+
+        // valid when mType is TYPE_ID
+        private final int mId;
+
+        // valid when mType is TYPE_SCALE
+        private final int mInterpolatorType;
+        private final int mOptionFlags;
+        private final double mDurationMs;
+        private final float[] mTimes;
+        private final float[] mVolumes;
+
+        @Override
+        public String toString() {
+            return "VolumeShaper.Configuration["
+                    + "mType=" + mType
+                    + (mType == TYPE_ID
+                    ? ",mId" + mId
+                    : ",mInterpolatorType=" + mInterpolatorType
+                    + ",mOptionFlags=" + mOptionFlags
+                    + ",mDurationMs=" + mDurationMs
+                    + ",mTimes[]=" + mTimes
+                    + ",mVolumes[]=" + mVolumes
+                    + "]");
+        }
+
+        @Override
+        public int hashCode() {
+            return mType == TYPE_ID
+                    ? Objects.hash(mType, mId)
+                    : Objects.hash(mType, mInterpolatorType, mDurationMs, mTimes, mVolumes);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Configuration)) return false;
+            if (o == this) return true;
+            final Configuration other = (Configuration) o;
+            return mType == other.mType &&
+                    (mType == TYPE_ID ? mId == other.mId
+                    : mInterpolatorType == other.mInterpolatorType
+                    && mDurationMs == other.mDurationMs
+                    && mTimes == other.mTimes
+                    && mVolumes == other.mVolumes);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mType);
+            dest.writeInt(mId);
+            if (mType != TYPE_ID) {
+                dest.writeInt(mInterpolatorType);
+                dest.writeInt(mOptionFlags);
+                dest.writeDouble(mDurationMs);
+                dest.writeFloatArray(mTimes);
+                dest.writeFloatArray(mVolumes);
+            }
+        }
+
+        public static final Parcelable.Creator<VolumeShaper.Configuration> CREATOR
+                = new Parcelable.Creator<VolumeShaper.Configuration>() {
+            @Override
+            public VolumeShaper.Configuration createFromParcel(Parcel p) {
+                final int type = p.readInt();
+                final int id = p.readInt();
+                if (type == TYPE_ID) {
+                    return new VolumeShaper.Configuration(id);
+                } else {
+                    return new VolumeShaper.Configuration(
+                        type,
+                        id,                    // id
+                        p.readInt(),           // interpolatorType
+                        p.readInt(),           // optionFlags
+                        p.readDouble(),        // durationMs
+                        p.createFloatArray(),  // times
+                        p.createFloatArray()); // volumes
+                }
+            }
+
+            @Override
+            public VolumeShaper.Configuration[] newArray(int size) {
+                return new VolumeShaper.Configuration[size];
+            }
+        };
+
+        /**
+         * Constructs a volume shaper from an id.
+         *
+         * This is an opaque handle for controlling a {@code VolumeShaper} that has
+         * already been sent to a player.  The {@code id} is returned from the
+         * initial {@code setVolumeShaper()} call on success.
+         *
+         * These configurations are for native use only,
+         * they are never returned directly to the user.
+         *
+         * @param id
+         * @throws IllegalArgumentException if id is negative.
+         */
+        private Configuration(int id) {
+            if (id < 0) {
+                throw new IllegalArgumentException("negative id " + id);
+            }
+            mType = TYPE_ID;
+            mId = id;
+            mInterpolatorType = 0;
+            mOptionFlags = 0;
+            mDurationMs = 0;
+            mTimes = null;
+            mVolumes = null;
+        }
+
+        /**
+         * Direct constructor for VolumeShaper.
+         * Use the Builder instead.
+         */
+        private Configuration(@Type int type,
+                int id,
+                @InterpolatorType int interpolatorType,
+                @OptionFlag int optionFlags,
+                double durationMs,
+                @NonNull float[] times,
+                @NonNull float[] volumes) {
+            mType = type;
+            mId = id;
+            mInterpolatorType = interpolatorType;
+            mOptionFlags = optionFlags;
+            mDurationMs = durationMs;
+            // Builder should have cloned these arrays already.
+            mTimes = times;
+            mVolumes = volumes;
+        }
+
+        /**
+         * Returns the {@code VolumeShaper} type.
+         */
+        public @Type int getType() {
+            return mType;
+        }
+
+        /**
+         * @hide
+         * Returns the {@code VolumeShaper} id.
+         */
+        public int getId() {
+            return mId;
+        }
+
+        /**
+         * Returns the interpolator type.
+         */
+        public @InterpolatorType int getInterpolatorType() {
+            return mInterpolatorType;
+        }
+
+        /**
+         * Returns the option flags
+         */
+        public @OptionFlag int getOptionFlags() {
+            return mOptionFlags & OPTION_FLAG_PUBLIC_ALL;
+        }
+
+        /* package */ @OptionFlag int getAllOptionFlags() {
+            return mOptionFlags;
+        }
+
+        /**
+         * Returns the duration of the effect in milliseconds.
+         */
+        public double getDurationMs() {
+            return mDurationMs;
+        }
+
+        /**
+         * Returns the times (x) coordinate array of the volume curve points.
+         */
+        public float[] getTimes() {
+            return mTimes;
+        }
+
+        /**
+         * Returns the volumes (y) coordinate array of the volume curve points.
+         */
+        public float[] getVolumes() {
+            return mVolumes;
+        }
+
+        /**
+         * Checks the validity of times and volumes point representation.
+         *
+         * {@code times[]} and {@code volumes[]} are two arrays representing points
+         * for the volume curve.
+         *
+         * @param times the x coordinates for the points,
+         *        must be between 0.f and 1.f and be monotonic.
+         * @param volumes the y coordinates for the points,
+         *        must be between 0.f and 1.f for linear and
+         *        must be no greater than 0.f for log (dBFS).
+         * @param log set to true if the scale is logarithmic.
+         * @return null if no error, or the reason in a {@code String} for an error.
+         */
+        private static @Nullable String checkCurveForErrors(
+                @NonNull float[] times, @NonNull float[] volumes, boolean log) {
+            if (times.length != volumes.length) {
+                return "array length must match";
+            } else if (times.length < 2) {
+                return "array length must be at least 2";
+            } else if (times.length > MAXIMUM_CURVE_POINTS) {
+                return "array length must be no larger than " + MAXIMUM_CURVE_POINTS;
+            } else if (times[0] != 0.f) {
+                return "times must start at 0.f";
+            } else if (times[times.length - 1] != 1.f) {
+                return "times must end at 1.f";
+            }
+
+            // validate points along the curve
+            for (int i = 1; i < times.length; ++i) {
+                if (!(times[i] > times[i - 1]) /* handle nan */) {
+                    return "times not monotonic increasing, check index " + i;
+                }
+            }
+            if (log) {
+                for (int i = 0; i < volumes.length; ++i) {
+                    if (!(volumes[i] <= 0.f) /* handle nan */) {
+                        return "volumes for log scale cannot be positive, "
+                                + "check index " + i;
+                    }
+                }
+            } else {
+                for (int i = 0; i < volumes.length; ++i) {
+                    if (!(volumes[i] >= 0.f) || !(volumes[i] <= 1.f) /* handle nan */) {
+                        return "volumes for linear scale must be between 0.f and 1.f, "
+                                + "check index " + i;
+                    }
+                }
+            }
+            return null; // no errors
+        }
+
+        private static void checkValidVolume(float volume, boolean log) {
+            if (log) {
+                if (!(volume <= 0.f) /* handle nan */) {
+                    throw new IllegalArgumentException("dbfs volume must be 0.f or less");
+                }
+            } else {
+                if (!(volume >= 0.f) || !(volume <= 1.f) /* handle nan */) {
+                    throw new IllegalArgumentException("volume must be >= 0.f and <= 1.f");
+                }
+            }
+        }
+
+        private static void clampVolume(float[] volumes, boolean log) {
+            if (log) {
+                for (int i = 0; i < volumes.length; ++i) {
+                    if (!(volumes[i] <= 0.f) /* handle nan */) {
+                        volumes[i] = 0.f;
+                    }
+                }
+            } else {
+                for (int i = 0; i < volumes.length; ++i) {
+                    if (!(volumes[i] >= 0.f) /* handle nan */) {
+                        volumes[i] = 0.f;
+                    } else if (!(volumes[i] <= 1.f)) {
+                        volumes[i] = 1.f;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Builder class for a {@link VolumeShaper.Configuration} object.
+         * <p> Here is an example where {@code Builder} is used to define the
+         * {@link VolumeShaper.Configuration}.
+         *
+         * <pre class="prettyprint">
+         * VolumeShaper.Configuration LINEAR_RAMP =
+         *         new VolumeShaper.Configuration.Builder()
+         *             .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
+         *             .setCurve(new float[] { 0.f, 1.f }, // times
+         *                       new float[] { 0.f, 1.f }) // volumes
+         *             .setDurationMs(1000.)
+         *             .build();
+         * </pre>
+         * <p>
+         */
+        public static final class Builder {
+            private int mType = TYPE_SCALE;
+            private int mId = -1; // invalid
+            private int mInterpolatorType = INTERPOLATOR_TYPE_CUBIC;
+            private int mOptionFlags = 0;
+            private double mDurationMs = 1000.;
+            private float[] mTimes = null;
+            private float[] mVolumes = null;
+
+            /**
+             * Constructs a new Builder with the defaults.
+             */
+            public Builder() {
+            }
+
+            /**
+             * Constructs a new Builder from a given {@code VolumeShaper.Configuration}
+             * @param configuration prototypical configuration
+             *        which will be reused in the new Builder.
+             */
+            public Builder(@NonNull Configuration configuration) {
+                mType = configuration.getType();
+                mId = configuration.getId();
+                mOptionFlags = configuration.getAllOptionFlags();
+                mInterpolatorType = configuration.getInterpolatorType();
+                mDurationMs = configuration.getDurationMs();
+                mTimes = configuration.getTimes();
+                mVolumes = configuration.getVolumes();
+            }
+
+            /**
+             * @hide
+             * TODO make SystemApi
+             *
+             * Set the id for system defined shapers.
+             * @param id
+             * @return
+             */
+            public @NonNull Builder setId(int id) {
+                mId = id;
+                return this;
+            }
+
+            /**
+             * Sets the interpolator type.
+             *
+             * If omitted the interplator type is {@link #INTERPOLATOR_TYPE_CUBIC}.
+             *
+             * @param interpolatorType method of interpolation used for the volume curve.
+             * @return the same Builder instance.
+             * @throws IllegalArgumentException if {@code interpolatorType} is not valid.
+             */
+            public @NonNull Builder setInterpolatorType(@InterpolatorType int interpolatorType) {
+                switch (interpolatorType) {
+                    case INTERPOLATOR_TYPE_STEP:
+                    case INTERPOLATOR_TYPE_LINEAR:
+                    case INTERPOLATOR_TYPE_CUBIC:
+                    case INTERPOLATOR_TYPE_CUBIC_MONOTONIC:
+                        mInterpolatorType = interpolatorType;
+                        break;
+                    default:
+                        throw new IllegalArgumentException("invalid interpolatorType: "
+                                + interpolatorType);
+                }
+                return this;
+            }
+
+            /**
+             * Sets the optional flags
+             *
+             * If omitted, flags are 0. If {@link #OPTION_FLAG_VOLUME_IN_DBFS} has
+             * changed the volume curve needs to be set again as the acceptable
+             * volume domain has changed.
+             *
+             * @param optionFlags new value to replace the old {@code optionFlags}.
+             * @return the same Builder instance.
+             * @throws IllegalArgumentException if flag is not recognized.
+             */
+            public @NonNull Builder setOptionFlags(@OptionFlag int optionFlags) {
+                if ((optionFlags & ~OPTION_FLAG_PUBLIC_ALL) != 0) {
+                    throw new IllegalArgumentException("invalid bits in flag: " + optionFlags);
+                }
+                mOptionFlags = mOptionFlags & ~OPTION_FLAG_PUBLIC_ALL | optionFlags;
+                return this;
+            }
+
+            /**
+             * Sets the volume shaper duration in milliseconds.
+             *
+             * If omitted, the default duration is 1 second.
+             *
+             * @param durationMs
+             * @return the same Builder instance.
+             * @throws IllegalArgumentException if duration is not positive.
+             */
+            public @NonNull Builder setDurationMs(double durationMs) {
+                if (durationMs <= 0.) {
+                    throw new IllegalArgumentException(
+                            "duration: " + durationMs + " not positive");
+                }
+                mDurationMs = durationMs;
+                return this;
+            }
+
+            /**
+             * Sets the volume curve.
+             *
+             * The volume curve is represented by a set of control points given by
+             * two float arrays of equal length,
+             * one representing the time (x) coordinates
+             * and one corresponding to the volume (y) coordinates.
+             * The length must be at least 2
+             * and no greater than {@link VolumeShaper.Configuration#getMaximumCurvePoints()}.
+             * <p>
+             * The volume curve is normalized as follows:
+             * (1) time (x) coordinates should be monotonically increasing, from 0.f to 1.f;
+             * (2) volume (y) coordinates must be within 0.f to 1.f for linear and be non-positive
+             *     for log scaling.
+             * <p>
+             * The time scale is set by {@link #setDurationMs} in seconds.
+             * <p>
+             * @param times an array of float values representing
+             *        the time line of the volume curve.
+             * @param volumes an array of float values representing
+             *        the amplitude of the volume curve.
+             * @return the same Builder instance.
+             * @throws IllegalArgumentException if {@code times} or {@code volumes} is invalid.
+             */
+            public @NonNull Builder setCurve(@NonNull float[] times, @NonNull float[] volumes) {
+                String error = checkCurveForErrors(
+                        times, volumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
+                if (error != null) {
+                    throw new IllegalArgumentException(error);
+                }
+                mTimes = times.clone();
+                mVolumes = volumes.clone();
+                return this;
+            }
+
+            /**
+             * Reflects the volume curve so that
+             * the shaper changes volume from the end
+             * to the start.
+             *
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder reflectTimes() {
+                int i;
+                for (i = 0; i < mTimes.length / 2; ++i) {
+                    float temp = mTimes[0];
+                    mTimes[i] = 1.f - mTimes[mTimes.length - 1 - i];
+                    mTimes[mTimes.length - 1 - i] = 1.f - temp;
+                }
+                if ((mTimes.length & 1) != 0) {
+                    mTimes[i] = 1.f - mTimes[i];
+                }
+                return this;
+            }
+
+            /**
+             * Inverts the volume curve so that the max volume
+             * becomes the min volume and vice versa.
+             *
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder invertVolumes() {
+                if (mVolumes.length >= 2) {
+                    float min = mVolumes[0];
+                    float max = mVolumes[0];
+                    for (int i = 1; i < mVolumes.length; ++i) {
+                        if (mVolumes[i] < min) {
+                            min = mVolumes[i];
+                        } else if (mVolumes[i] > max) {
+                            max = mVolumes[i];
+                        }
+                    }
+
+                    final float maxmin = max + min;
+                    for (int i = 0; i < mVolumes.length; ++i) {
+                        mVolumes[i] = maxmin - mVolumes[i];
+                    }
+                }
+                return this;
+            }
+
+            /**
+             * Scale the curve end volume to a target value.
+             *
+             * Keeps the start volume the same.
+             * This works best if the volume curve is monotonic.
+             *
+             * @return the same Builder instance.
+             * @throws IllegalArgumentException if volume is not valid.
+             */
+            public @NonNull Builder scaleToEndVolume(float volume) {
+                final boolean log = (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0;
+                checkValidVolume(volume, log);
+                final float startVolume = mVolumes[0];
+                final float endVolume = mVolumes[mVolumes.length - 1];
+                if (endVolume == startVolume) {
+                    // match with linear ramp
+                    final float offset = volume - startVolume;
+                    for (int i = 0; i < mVolumes.length; ++i) {
+                        mVolumes[i] = mVolumes[i] + offset * mTimes[i];
+                    }
+                } else {
+                    // scale
+                    final float scale = (volume - startVolume) / (endVolume - startVolume);
+                    for (int i = 0; i < mVolumes.length; ++i) {
+                        mVolumes[i] = scale * (mVolumes[i] - startVolume) + startVolume;
+                    }
+                }
+                clampVolume(mVolumes, log);
+                return this;
+            }
+
+            /**
+             * Scale the curve start volume to a target value.
+             *
+             * Keeps the end volume the same.
+             * This works best if the volume curve is monotonic.
+             *
+             * @return the same Builder instance.
+             * @throws IllegalArgumentException if volume is not valid.
+             */
+            public @NonNull Builder scaleToStartVolume(float volume) {
+                final boolean log = (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0;
+                checkValidVolume(volume, log);
+                final float startVolume = mVolumes[0];
+                final float endVolume = mVolumes[mVolumes.length - 1];
+                if (endVolume == startVolume) {
+                    // match with linear ramp
+                    final float offset = volume - startVolume;
+                    for (int i = 0; i < mVolumes.length; ++i) {
+                        mVolumes[i] = mVolumes[i] + offset * (1.f - mTimes[i]);
+                    }
+                } else {
+                    final float scale = (volume - endVolume) / (startVolume - endVolume);
+                    for (int i = 0; i < mVolumes.length; ++i) {
+                        mVolumes[i] = scale * (mVolumes[i] - endVolume) + endVolume;
+                    }
+                }
+                clampVolume(mVolumes, log);
+                return this;
+            }
+
+            /**
+             * Builds a new {@link VolumeShaper} object.
+             *
+             * @return a new {@link VolumeShaper} object
+             */
+            public @NonNull Configuration build() {
+                String error = checkCurveForErrors(
+                        mTimes, mVolumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
+                if (error != null) {
+                    throw new IllegalArgumentException(error);
+                }
+                return new Configuration(mType, mId, mInterpolatorType, mOptionFlags,
+                        mDurationMs, mTimes, mVolumes);
+            }
+        } // Configuration.Builder
+    } // Configuration
+
+    /**
+     * The {@code VolumeShaper.Operation} class is used to specify operations
+     * to the {@code VolumeShaper} that affect the volume change.
+     */
+    public static final class Operation implements Parcelable {
+        /**
+         * Forward playback from current volume time position.
+         */
+        public static final Operation PLAY =
+                new VolumeShaper.Operation.Builder()
+                    .build();
+
+        /**
+         * Reverse playback from current volume time position.
+         */
+        public static final Operation REVERSE =
+                new VolumeShaper.Operation.Builder()
+                    .reverse()
+                    .build();
+
+        // No user serviceable parts below.
+
+        // These flags must match the native VolumeShaper::Operation::Flag
+        /** @hide */
+        @IntDef({
+            FLAG_NONE,
+            FLAG_REVERSE,
+            FLAG_TERMINATE,
+            FLAG_JOIN,
+            FLAG_DEFER,
+            })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Flag {}
+
+        /**
+         * No special {@code VolumeShaper} operation.
+         */
+        private static final int FLAG_NONE = 0;
+
+        /**
+         * Reverse the {@code VolumeShaper} progress.
+         *
+         * Reverses the {@code VolumeShaper} curve from its current
+         * position. If the {@code VolumeShaper} curve has not started,
+         * it automatically is considered finished.
+         */
+        private static final int FLAG_REVERSE = 1 << 0;
+
+        /**
+         * Terminate the existing {@code VolumeShaper}.
+         * This flag is generally used by itself;
+         * it takes precedence over all other flags.
+         */
+        private static final int FLAG_TERMINATE = 1 << 1;
+
+        /**
+         * Attempt to join as best as possible to the previous {@code VolumeShaper}.
+         * This requires the previous {@code VolumeShaper} to be active and
+         * {@link #setReplaceId} to be set.
+         */
+        private static final int FLAG_JOIN = 1 << 2;
+
+        /**
+         * Defer playback until next operation is sent. This is used
+         * when starting a VolumeShaper effect.
+         */
+        private static final int FLAG_DEFER = 1 << 3;
+
+        private static final int FLAG_PUBLIC_ALL = FLAG_REVERSE | FLAG_TERMINATE;
+
+        private final int mFlags;
+        private final int mReplaceId;
+
+        @Override
+        public String toString() {
+            return "VolumeShaper.Operation["
+                    + "mFlags=" + mFlags
+                    + ",mReplaceId" + mReplaceId
+                    + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFlags, mReplaceId);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Operation)) return false;
+            if (o == this) return true;
+            final Operation other = (Operation) o;
+            return mFlags == other.mFlags
+                    && mReplaceId == other.mReplaceId;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mFlags);
+            dest.writeInt(mReplaceId);
+        }
+
+        public static final Parcelable.Creator<VolumeShaper.Operation> CREATOR
+                = new Parcelable.Creator<VolumeShaper.Operation>() {
+            @Override
+            public VolumeShaper.Operation createFromParcel(Parcel p) {
+                return new VolumeShaper.Operation(
+                        p.readInt()     // flags
+                        , p.readInt()); // replaceId
+            }
+
+            @Override
+            public VolumeShaper.Operation[] newArray(int size) {
+                return new VolumeShaper.Operation[size];
+            }
+        };
+
+        private Operation(@Flag int flags, int replaceId) {
+            mFlags = flags;
+            mReplaceId = replaceId;
+        }
+
+        /**
+         * @hide
+         * {@code Builder} class for {@link VolumeShaper.Operation} object.
+         *
+         * Not for public use.
+         */
+        public static final class Builder {
+            int mFlags;
+            int mReplaceId;
+
+            /**
+             * Constructs a new {@code Builder} with the defaults.
+             */
+            public Builder() {
+                mFlags = 0;
+                mReplaceId = -1;
+            }
+
+            /**
+             * Constructs a new Builder from a given {@code VolumeShaper.Operation}
+             * @param operation the {@code VolumeShaper.operation} whose data will be
+             *        reused in the new Builder.
+             */
+            public Builder(@NonNull VolumeShaper.Operation operation) {
+                mReplaceId = operation.mReplaceId;
+                mFlags = operation.mFlags;
+            }
+
+            /**
+             * Replaces the previous {@code VolumeShaper}.
+             * It has no other effect if the {@code VolumeShaper} is
+             * already expired. If the replaceId is the same as the id associated with
+             * the {@code VolumeShaper} in a {@code setVolumeShaper()} call,
+             * an error is returned.
+             * @param handle is a previous volumeShaper {@code VolumeShaper}.
+             * @param join the start to match the current volume of the previous
+             * shaper.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder replace(int id, boolean join) {
+                mReplaceId = id;
+                if (join) {
+                    mFlags |= FLAG_JOIN;
+                } else {
+                    mFlags &= ~FLAG_JOIN;
+                }
+                return this;
+            }
+
+            /**
+             * Defers all operations.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder defer() {
+                mFlags |= FLAG_DEFER;
+                return this;
+            }
+
+            /**
+             * Terminates the VolumeShaper.
+             * Do not call directly, use {@link VolumeShaper#release()}.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder terminate() {
+                mFlags |= FLAG_TERMINATE;
+                return this;
+            }
+
+            /**
+             * Reverses direction.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder reverse() {
+                mFlags ^= FLAG_REVERSE;
+                return this;
+            }
+
+            /**
+             * Sets the operation flag.  Do not call this directly but one of the
+             * other builder methods.
+             *
+             * @param flags new value for {@code flags}, consisting of ORed flags.
+             * @return the same Builder instance.
+             */
+            private @NonNull Builder setFlags(@Flag int flags) {
+                if ((flags & ~FLAG_PUBLIC_ALL) != 0) {
+                    throw new IllegalArgumentException("flag has unknown bits set: " + flags);
+                }
+                mFlags = mFlags & ~FLAG_PUBLIC_ALL | flags;
+                return this;
+            }
+
+            /**
+             * Builds a new {@link VolumeShaper.Operation} object.
+             *
+             * @return a new {@code VolumeShaper.Operation} object
+             */
+            public @NonNull Operation build() {
+                return new Operation(mFlags, mReplaceId);
+            }
+        } // Operation.Builder
+    } // Operation
+
+    /**
+     * @hide
+     * {@code VolumeShaper.State} represents the current progress
+     * of the {@code VolumeShaper}.
+     *
+     *  Not for public use.
+     */
+    public static final class State implements Parcelable {
+        private float mVolume;
+        private float mXOffset;
+
+        @Override
+        public String toString() {
+            return "VolumeShaper.State["
+                    + "mVolume=" + mVolume
+                    + ",mXOffset" + mXOffset
+                    + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mVolume, mXOffset);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof State)) return false;
+            if (o == this) return true;
+            final State other = (State) o;
+            return mVolume == other.mVolume
+                    && mXOffset == other.mXOffset;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeFloat(mVolume);
+            dest.writeFloat(mXOffset);
+        }
+
+        public static final Parcelable.Creator<VolumeShaper.State> CREATOR
+                = new Parcelable.Creator<VolumeShaper.State>() {
+            @Override
+            public VolumeShaper.State createFromParcel(Parcel p) {
+                return new VolumeShaper.State(
+                        p.readFloat()     // volume
+                        , p.readFloat()); // xOffset
+            }
+
+            @Override
+            public VolumeShaper.State[] newArray(int size) {
+                return new VolumeShaper.State[size];
+            }
+        };
+
+        /* package */ State(float volume, float xOffset) {
+            mVolume = volume;
+            mXOffset = xOffset;
+        }
+
+        /**
+         * Gets the volume of the {@link VolumeShaper.State}.
+         */
+        public float getVolume() {
+            return mVolume;
+        }
+
+        /**
+         * Gets the elapsed ms of the {@link VolumeShaper.State}
+         */
+        public double getXOffset() {
+            return mXOffset;
+        }
+    } // State
+}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5e8135f..c941766 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -44,6 +44,7 @@
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
+#include "android_media_VolumeShaper.h"
 #include "android_media_Utils.h"
 
 #include "android_os_Parcel.h"
@@ -160,6 +161,7 @@
 static BufferingParams::fields_t gBufferingParamsFields;
 static PlaybackParams::fields_t gPlaybackParamsFields;
 static SyncParams::fields_t gSyncParamsFields;
+static VolumeShaperHelper::fields_t gVolumeShaperFields;
 
 static Mutex sLock;
 
@@ -1088,6 +1090,7 @@
 
     gPlaybackParamsFields.init(env);
     gSyncParamsFields.init(env);
+    gVolumeShaperFields.init(env);
 }
 
 static void
@@ -1259,6 +1262,51 @@
     ;
 }
 
+// Pass through the arguments to the MediaServer player implementation.
+static jint android_media_MediaPlayer_applyVolumeShaper(JNIEnv *env, jobject thiz,
+        jobject jconfig, jobject joperation) {
+    // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
+    const int VOLUME_SHAPER_INVALID_OPERATION = -38;
+
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == nullptr) {
+        return (jint)VOLUME_SHAPER_INVALID_OPERATION;
+    }
+
+    sp<VolumeShaper::Configuration> configuration;
+    sp<VolumeShaper::Operation> operation;
+    if (jconfig != nullptr) {
+        configuration = VolumeShaperHelper::convertJobjectToConfiguration(
+                env, gVolumeShaperFields, jconfig);
+        ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
+    }
+    if (joperation != nullptr) {
+        operation = VolumeShaperHelper::convertJobjectToOperation(
+                env, gVolumeShaperFields, joperation);
+        ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
+    }
+    VolumeShaper::Status status = mp->applyVolumeShaper(configuration, operation);
+    if (status == INVALID_OPERATION) {
+        status = VOLUME_SHAPER_INVALID_OPERATION;
+    }
+    return (jint)status; // if status < 0 an error, else a VolumeShaper id
+}
+
+// Pass through the arguments to the MediaServer player implementation.
+static jobject android_media_MediaPlayer_getVolumeShaperState(JNIEnv *env, jobject thiz,
+        jint id) {
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == nullptr) {
+        return (jobject)nullptr;
+    }
+
+    sp<VolumeShaper::State> state = mp->getVolumeShaperState((int)id);
+    if (state.get() == nullptr) {
+        return (jobject)nullptr;
+    }
+    return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
+}
+
 /////////////////////////////////////////////////////////////////////////////////////
 // Modular DRM begin
 
@@ -1747,6 +1795,12 @@
     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
     {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
     {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},
+    {"native_applyVolumeShaper",
+                            "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
+                                                                (void *)android_media_MediaPlayer_applyVolumeShaper},
+    {"native_getVolumeShaperState",
+                            "(I)Landroid/media/VolumeShaper$State;",
+                                                                (void *)android_media_MediaPlayer_getVolumeShaperState},
     // Modular DRM
     { "_prepareDrm", "([BI)V",                                  (void *)android_media_MediaPlayer_prepareDrm },
     { "_releaseDrm", "()V",                                     (void *)android_media_MediaPlayer_releaseDrm },
diff --git a/media/jni/android_media_VolumeShaper.h b/media/jni/android_media_VolumeShaper.h
new file mode 100644
index 0000000..dbbc478
--- /dev/null
+++ b/media/jni/android_media_VolumeShaper.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2017, 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 _ANDROID_MEDIA_VOLUME_SHAPER_H_
+#define _ANDROID_MEDIA_VOLUME_SHAPER_H_
+
+#include <media/VolumeShaper.h>
+
+namespace android {
+
+// This entire class is inline as it is used from both core and media
+struct VolumeShaperHelper {
+    struct fields_t {
+        // VolumeShaper.Configuration
+        jclass    coClazz;
+        jmethodID coConstructId;
+        jfieldID  coTypeId;
+        jfieldID  coIdId;
+        jfieldID  coInterpolatorTypeId;
+        jfieldID  coOptionFlagsId;
+        jfieldID  coDurationMsId;
+        jfieldID  coTimesId;
+        jfieldID  coVolumesId;
+
+        // VolumeShaper.Operation
+        jclass    opClazz;
+        jmethodID opConstructId;
+        jfieldID  opFlagsId;
+        jfieldID  opReplaceIdId;
+
+        // VolumeShaper.State
+        jclass    stClazz;
+        jmethodID stConstructId;
+        jfieldID  stVolumeId;
+        jfieldID  stXOffsetId;
+
+        void init(JNIEnv *env) {
+            jclass lclazz = env->FindClass("android/media/VolumeShaper$Configuration");
+            if (lclazz == nullptr) {
+                return;
+            }
+            coClazz = (jclass)env->NewGlobalRef(lclazz);
+            if (coClazz == nullptr) {
+                return;
+            }
+            coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIID[F[F)V");
+            coTypeId = env->GetFieldID(coClazz, "mType", "I");
+            coIdId = env->GetFieldID(coClazz, "mId", "I");
+            coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
+            coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");
+            coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");
+            coTimesId = env->GetFieldID(coClazz, "mTimes", "[F");
+            coVolumesId = env->GetFieldID(coClazz, "mVolumes", "[F");
+            env->DeleteLocalRef(lclazz);
+
+            lclazz = env->FindClass("android/media/VolumeShaper$Operation");
+            if (lclazz == nullptr) {
+                return;
+            }
+            opClazz = (jclass)env->NewGlobalRef(lclazz);
+            if (opClazz == nullptr) {
+                return;
+            }
+            opConstructId = env->GetMethodID(opClazz, "<init>", "(II)V");
+            opFlagsId = env->GetFieldID(opClazz, "mFlags", "I");
+            opReplaceIdId = env->GetFieldID(opClazz, "mReplaceId", "I");
+            env->DeleteLocalRef(lclazz);
+
+            lclazz = env->FindClass("android/media/VolumeShaper$State");
+            if (lclazz == nullptr) {
+                return;
+            }
+            stClazz = (jclass)env->NewGlobalRef(lclazz);
+            if (stClazz == nullptr) {
+                return;
+            }
+            stConstructId = env->GetMethodID(stClazz, "<init>", "(FF)V");
+            stVolumeId = env->GetFieldID(stClazz, "mVolume", "F");
+            stXOffsetId = env->GetFieldID(stClazz, "mXOffset", "F");
+            env->DeleteLocalRef(lclazz);
+        }
+
+        void exit(JNIEnv *env) {
+            env->DeleteGlobalRef(coClazz);
+            coClazz = nullptr;
+        }
+    };
+
+    static sp<VolumeShaper::Configuration> convertJobjectToConfiguration(
+            JNIEnv *env, const fields_t &fields, jobject jshaper) {
+        sp<VolumeShaper::Configuration> configuration = new VolumeShaper::Configuration();
+
+        configuration->setType(
+            (VolumeShaper::Configuration::Type)env->GetIntField(jshaper, fields.coTypeId));
+        configuration->setId(
+            (int)env->GetIntField(jshaper, fields.coIdId));
+        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
+            configuration->setInterpolatorType(
+                (VolumeShaper::Configuration::InterpolatorType)
+                env->GetIntField(jshaper, fields.coInterpolatorTypeId));
+            configuration->setOptionFlags(
+                (VolumeShaper::Configuration::OptionFlag)
+                env->GetIntField(jshaper, fields.coOptionFlagsId));
+            configuration->setDurationMs(
+                    (double)env->GetDoubleField(jshaper, fields.coDurationMsId));
+
+            // convert point arrays
+            jobject xobj = env->GetObjectField(jshaper, fields.coTimesId);
+            jfloatArray *xarray = reinterpret_cast<jfloatArray*>(&xobj);
+            jsize xlen = env->GetArrayLength(*xarray);
+            /* const */ float * const x =
+                    env->GetFloatArrayElements(*xarray, nullptr /* isCopy */);
+            jobject yobj = env->GetObjectField(jshaper, fields.coVolumesId);
+            jfloatArray *yarray = reinterpret_cast<jfloatArray*>(&yobj);
+            jsize ylen = env->GetArrayLength(*yarray);
+            /* const */ float * const y =
+                    env->GetFloatArrayElements(*yarray, nullptr /* isCopy */);
+            if (xlen != ylen) {
+                ALOGE("array size must match");
+                return nullptr;
+            }
+            for (jsize i = 0; i < xlen; ++i) {
+                configuration->emplace(x[i], y[i]);
+            }
+            env->ReleaseFloatArrayElements(*xarray, x, JNI_ABORT); // no need to copy back
+            env->ReleaseFloatArrayElements(*yarray, y, JNI_ABORT);
+        }
+        return configuration;
+    }
+
+    static jobject convertVolumeShaperToJobject(
+            JNIEnv *env, const fields_t &fields,
+            const sp<VolumeShaper::Configuration> &configuration) {
+        jfloatArray xarray = nullptr;
+        jfloatArray yarray = nullptr;
+        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
+            // convert curve arrays
+            jfloatArray xarray = env->NewFloatArray(configuration->size());
+            jfloatArray yarray = env->NewFloatArray(configuration->size());
+            float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
+            float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
+            float *xptr = x, *yptr = y;
+            for (const auto &pt : *configuration.get()) {
+                *xptr++ = pt.first;
+                *yptr++ = pt.second;
+            }
+            env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
+            env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
+        }
+
+        // prepare constructor args
+        jvalue args[7];
+        args[0].i = (jint)configuration->getType();
+        args[1].i = (jint)configuration->getId();
+        args[2].i = (jint)configuration->getInterpolatorType();
+        args[3].i = (jint)configuration->getOptionFlags();
+        args[4].d = (jdouble)configuration->getDurationMs();
+        args[5].l = xarray;
+        args[6].l = yarray;
+        jobject jshaper = env->NewObjectA(fields.coClazz, fields.coConstructId, args);
+        return jshaper;
+    }
+
+    static sp<VolumeShaper::Operation> convertJobjectToOperation(
+            JNIEnv *env, const fields_t &fields, jobject joperation) {
+        VolumeShaper::Operation::Flag flags =
+            (VolumeShaper::Operation::Flag)env->GetIntField(joperation, fields.opFlagsId);
+        int replaceId = env->GetIntField(joperation, fields.opReplaceIdId);
+
+        sp<VolumeShaper::Operation> operation = new VolumeShaper::Operation(flags, replaceId);
+        return operation;
+    }
+
+    static jobject convertOperationToJobject(
+            JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::Operation> &operation) {
+        // prepare constructor args
+        jvalue args[2];
+        args[0].i = (jint)operation->getFlags();
+        args[1].i = (jint)operation->getReplaceId();
+
+        jobject joperation = env->NewObjectA(fields.opClazz, fields.opConstructId, args);
+        return joperation;
+    }
+
+    static sp<VolumeShaper::State> convertJobjectToState(
+            JNIEnv *env, const fields_t &fields, jobject jstate) {
+        float volume = env->GetFloatField(jstate, fields.stVolumeId);
+        float xOffset = env->GetFloatField(jstate, fields.stXOffsetId);
+
+        sp<VolumeShaper::State> state = new VolumeShaper::State(volume, xOffset);
+        return state;
+    }
+
+    static jobject convertStateToJobject(
+            JNIEnv *env, const fields_t &fields, const sp<VolumeShaper::State> &state) {
+        // prepare constructor args
+        jvalue args[2];
+        args[0].f = (jfloat)state->getVolume();
+        args[1].f = (jfloat)state->getXOffset();
+
+        jobject jstate = env->NewObjectA(fields.stClazz, fields.stConstructId, args);
+        return jstate;
+    }
+};
+
+}  // namespace android
+
+#endif // _ANDROID_MEDIA_VOLUME_SHAPER_H_
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 561d924..1f03b51 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1504,4 +1504,20 @@
             return isMusicApp;
         }
     };
+
+    public static final AppFilter FILTER_OTHER_APPS = new AppFilter() {
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            boolean isCategorized;
+            synchronized(entry) {
+                isCategorized = entry.info.category == ApplicationInfo.CATEGORY_AUDIO ||
+                    entry.info.category == ApplicationInfo.CATEGORY_GAME;
+            }
+            return !isCategorized;
+        }
+    };
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 6010621..9ad0b3e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -322,6 +322,7 @@
         }
     }
 
+    @Deprecated
     public boolean openTile(Tile tile) {
         closeDrawer();
         if (tile == null) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 8a6ae86..80e1cbf 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -86,4 +86,25 @@
 
         assertThat(ApplicationsState.FILTER_AUDIO.filterApp(mEntry)).isFalse();
     }
+
+    @Test
+    public void testOtherAppsRejectsAudio() {
+        mEntry.info.category = ApplicationInfo.CATEGORY_AUDIO;
+
+        assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
+    }
+
+    @Test
+    public void testOtherAppsRejectsGame() {
+        mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
+
+        assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse();
+    }
+
+    @Test
+    public void testOtherAppsAcceptsDefaultCategory() {
+        mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED;
+
+        assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isTrue();
+    }
 }
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 37ea537..17d0a09 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -860,15 +860,19 @@
             .append(SystemProperties.get("ro.build.description"))
             .append("\nSerial number: ")
             .append(SystemProperties.get("ro.serialno"));
+        int descriptionLength = 0;
         if (!TextUtils.isEmpty(info.description)) {
             messageBody.append("\nDescription: ").append(info.description);
+            descriptionLength = info.description.length();
         }
         intent.putExtra(Intent.EXTRA_TEXT, messageBody.toString());
         final ClipData clipData = new ClipData(null, new String[] { mimeType },
                 new ClipData.Item(null, null, null, bugreportUri));
+        Log.d(TAG, "share intent: bureportUri=" + bugreportUri);
         final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri);
         for (File screenshot : info.screenshotFiles) {
             final Uri screenshotUri = getUri(context, screenshot);
+            Log.d(TAG, "share intent: screenshotUri=" + screenshotUri);
             clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
             attachments.add(screenshotUri);
         }
@@ -887,6 +891,10 @@
             // default profile.
         }
 
+        // Log what was sent to the intent
+        Log.d(TAG, "share intent: EXTRA_SUBJECT=" + subject + ", EXTRA_TEXT=" + messageBody.length()
+                + " chars, description=" + descriptionLength + " chars");
+
         return intent;
     }
 
@@ -1266,14 +1274,17 @@
             return;
         }
         if (title != null && !title.equals(info.title)) {
+            Log.d(TAG, "updating bugreport title: " + title);
             MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_TITLE_CHANGED);
         }
         info.title = title;
         if (description != null && !description.equals(info.description)) {
+            Log.d(TAG, "updating bugreport description: " + description.length() + " chars");
             MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_DESCRIPTION_CHANGED);
         }
         info.description = description;
         if (name != null && !name.equals(info.name)) {
+            Log.d(TAG, "updating bugreport name: " + name);
             MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_NAME_CHANGED);
             info.name = name;
             updateProgress(info);
@@ -1684,17 +1695,34 @@
         public String toString() {
             final float percent = ((float) progress * 100 / max);
             final float realPercent = ((float) realProgress * 100 / realMax);
-            return "\tid: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished
-                    + "\n\ttitle: " + title
-                    + "\n\tdescription: " + description
-                    + "\n\tfile: " + bugreportFile
-                    + "\n\tscreenshots: " + screenshotFiles
-                    + "\n\tprogress: " + progress + "/" + max + " (" + percent + ")"
-                    + "\n\treal progress: " + realProgress + "/" + realMax + " (" + realPercent
-                    + ")"
-                    + "\n\tlast_update: " + getFormattedLastUpdate()
-                    + "\n\taddingDetailsToZip: " + addingDetailsToZip + " addedDetailsToZip: "
-                    + addedDetailsToZip;
+
+            final StringBuilder builder = new StringBuilder()
+                    .append("\tid: ").append(id)
+                    .append(", pid: ").append(pid)
+                    .append(", name: ").append(name)
+                    .append(", finished: ").append(finished)
+                    .append("\n\ttitle: ").append(title)
+                    .append("\n\tdescription: ");
+            if (description == null) {
+                builder.append("null");
+            } else {
+                if (TextUtils.getTrimmedLength(description) == 0) {
+                    builder.append("empty ");
+                }
+                builder.append("(").append(description.length()).append(" chars)");
+            }
+
+            return builder
+                .append("\n\tfile: ").append(bugreportFile)
+                .append("\n\tscreenshots: ").append(screenshotFiles)
+                .append("\n\tprogress: ").append(progress).append("/").append(max)
+                .append(" (").append(percent).append(")")
+                .append("\n\treal progress: ").append(realProgress).append("/").append(realMax)
+                .append(" (").append(realPercent).append(")")
+                .append("\n\tlast_update: ").append(getFormattedLastUpdate())
+                .append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
+                .append(" addedDetailsToZip: ").append(addedDetailsToZip)
+                .toString();
         }
 
         // Parcelable contract
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
new file mode 100644
index 0000000..d6abc47
--- /dev/null
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<!-- Loaded into BatteryMeterView as necessary -->
+<TextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/battery_percentage_view"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:textColor="?android:attr/textColorPrimary"
+        android:gravity="center_vertical|start"
+        android:paddingEnd="4dp"
+        />
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 3a33992..bfa92ad 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -31,9 +31,8 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
 
-    <!-- battery must be padded below to match assets -->
     <com.android.systemui.BatteryMeterView android:id="@+id/battery"
-        android:layout_height="@dimen/status_bar_battery_icon_height"
-        android:layout_width="@dimen/status_bar_battery_icon_width"
-        android:layout_marginBottom="@dimen/battery_margin_bottom"/>
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e737d2d..2102e07 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -69,8 +69,8 @@
     <!-- Height of a small notification in the status bar-->
     <dimen name="notification_min_height">92dp</dimen>
 
-    <!-- Height of a small notification in the status bar if it is a large (like messaging)-->
-    <dimen name="notification_min_height_large">132dp</dimen>
+    <!-- Increased height of a small notification in the status bar -->
+    <dimen name="notification_min_height_increased">132dp</dimen>
 
     <!-- Height of a small notification in the status bar which was used before android N -->
     <dimen name="notification_min_height_legacy">64dp</dimen>
@@ -87,6 +87,9 @@
     <!-- Height of a heads up notification in the status bar -->
     <dimen name="notification_max_heads_up_height">148dp</dimen>
 
+    <!-- Height of a heads up notification in the status bar -->
+    <dimen name="notification_max_heads_up_height_increased">188dp</dimen>
+
     <!-- a threshold in dp per second that is considered fast scrolling -->
     <dimen name="scroll_fast_threshold">1500dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
deleted file mode 100644
index 9068079..0000000
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ /dev/null
@@ -1,87 +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.
- */
-
-package com.android.systemui;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings;
-import com.android.settingslib.graph.BatteryMeterDrawableBase;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-public class BatteryMeterDrawable extends BatteryMeterDrawableBase implements
-        BatteryController.BatteryStateChangeCallback {
-
-    public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
-
-    private BatteryController mBatteryController;
-    private SettingObserver mSettingObserver;
-
-    public BatteryMeterDrawable(Context context, int frameColor) {
-        super(context, frameColor);
-
-        mSettingObserver = new SettingObserver(new Handler(mContext.getMainLooper()));
-    }
-
-    @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-        setBatteryLevel(level);
-        setPluggedIn(pluggedIn);
-    }
-
-    @Override
-    public void onPowerSaveChanged(boolean isPowerSave) {
-        setPowerSave(isPowerSave);
-    }
-
-    public void startListening() {
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
-        updateShowPercent();
-        mBatteryController.addCallback(this);
-    }
-
-    public void stopListening() {
-        mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
-        mBatteryController.removeCallback(this);
-    }
-
-    protected void updateShowPercent() {
-        setShowPercent(0 != Settings.System.getInt(mContext.getContentResolver(),
-                SHOW_PERCENT_SETTING, 0));
-    }
-
-    public void setBatteryController(BatteryController batteryController) {
-        mBatteryController = batteryController;
-        setPowerSave(mBatteryController.isPowerSave());
-    }
-
-    private final class SettingObserver extends ContentObserver {
-        public SettingObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            super.onChange(selfChange, uri);
-            updateShowPercent();
-            postInvalidate();
-        }
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 69e3874..f821308 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -23,10 +23,22 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.settingslib.graph.BatteryMeterDrawableBase;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -37,12 +49,22 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
-public class BatteryMeterView extends ImageView implements
+import java.text.NumberFormat;
+
+public class BatteryMeterView extends LinearLayout implements
         BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
 
-    private final BatteryMeterDrawable mDrawable;
+    public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
+
+    private final BatteryMeterDrawableBase mDrawable;
     private final String mSlotBattery;
+    private final ImageView mBatteryIconView;
+    private TextView mBatteryPercentView;
+
     private BatteryController mBatteryController;
+    private SettingObserver mSettingObserver;
+    private int mTextColor;
+    private int mLevel;
 
     public BatteryMeterView(Context context) {
         this(context, null, 0);
@@ -55,16 +77,35 @@
     public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
+        setOrientation(LinearLayout.HORIZONTAL);
+        setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
+
         TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
                 defStyle, 0);
         final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
                 context.getColor(R.color.batterymeter_frame_color));
-        mDrawable = new BatteryMeterDrawable(context, frameColor);
+        mDrawable = new BatteryMeterDrawableBase(context, frameColor);
         atts.recycle();
 
+        mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
+
         mSlotBattery = context.getString(
                 com.android.internal.R.string.status_bar_battery);
-        setImageDrawable(mDrawable);
+        mBatteryIconView = new ImageView(context);
+        mBatteryIconView.setImageDrawable(mDrawable);
+        final MarginLayoutParams mlp = new MarginLayoutParams(
+                getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width),
+                getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height));
+        mlp.setMargins(0, 0, 0,
+                getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom));
+        addView(mBatteryIconView, mlp);
+
+        updateShowPercent();
+    }
+
+    // StatusBarIconController reaches in here and adjusts the layout parameters of the icon
+    public ImageView getBatteryIconView() {
+        return mBatteryIconView;
     }
 
     @Override
@@ -84,9 +125,10 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         mBatteryController = Dependency.get(BatteryController.class);
-        mDrawable.setBatteryController(mBatteryController);
         mBatteryController.addCallback(this);
-        mDrawable.startListening();
+        getContext().getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
+        updateShowPercent();
         Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
@@ -95,13 +137,17 @@
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mBatteryController.removeCallback(this);
-        mDrawable.stopListening();
+        getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
         Dependency.get(TunerService.class).removeTunable(this);
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
     @Override
     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        mDrawable.setBatteryLevel(level);
+        mDrawable.setPluggedIn(pluggedIn);
+        mLevel = level;
+        updatePercentText();
         setContentDescription(
                 getContext().getString(charging ? R.string.accessibility_battery_level_charging
                         : R.string.accessibility_battery_level, level));
@@ -109,7 +155,41 @@
 
     @Override
     public void onPowerSaveChanged(boolean isPowerSave) {
+        mDrawable.setPowerSave(isPowerSave);
+    }
 
+    private TextView loadPercentView() {
+        return (TextView) LayoutInflater.from(getContext())
+                .inflate(R.layout.battery_percentage_view, null);
+    }
+
+    private void updatePercentText() {
+        if (mBatteryPercentView != null) {
+            mBatteryPercentView.setText(
+                    NumberFormat.getPercentInstance().format(mLevel/100f));
+        }
+    }
+
+    private void updateShowPercent() {
+        final boolean showing = mBatteryPercentView != null;
+        if (0 != Settings.System.getInt(getContext().getContentResolver(),
+                BatteryMeterView.SHOW_PERCENT_SETTING, 0)) {
+            if (!showing) {
+                mBatteryPercentView = loadPercentView();
+                if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
+                updatePercentText();
+                addView(mBatteryPercentView,
+                        0,
+                        new ViewGroup.LayoutParams(
+                                LayoutParams.WRAP_CONTENT,
+                                LayoutParams.MATCH_PARENT));
+            }
+        } else {
+            if (showing) {
+                removeView(mBatteryPercentView);
+                mBatteryPercentView = null;
+            }
+        }
     }
 
     @Override
@@ -133,17 +213,37 @@
 
         LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams(
                 (int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor));
-        scaledLayoutParams.setMarginsRelative(0, 0, 0, marginBottom);
+        scaledLayoutParams.setMargins(0, 0, 0, marginBottom);
 
-        setLayoutParams(scaledLayoutParams);
+        mBatteryIconView.setLayoutParams(scaledLayoutParams);
     }
 
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         mDrawable.setDarkIntensity(DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0);
+        setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+    }
+
+    public void setTextColor(int color) {
+        mTextColor = color;
+        if (mBatteryPercentView != null) {
+            mBatteryPercentView.setTextColor(color);
+        }
     }
 
     public void setRawColors(int fgColor, int bgColor) {
         mDrawable.setColors(fgColor, bgColor);
     }
+
+    private final class SettingObserver extends ContentObserver {
+        public SettingObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            updateShowPercent();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index 23eaed9..32b5862 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard;
 
 import static android.app.ActivityManager.TaskDescription;
-import static android.app.ActivityManager.StackId;
 
 import android.annotation.ColorInt;
 import android.annotation.UserIdInt;
@@ -32,13 +31,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Color;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.View;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Bouncer between work activities and the activity used to confirm credentials before unlocking
  * a managed profile.
@@ -51,52 +51,39 @@
     private static final String TAG = "WorkLockActivity";
 
     /**
-     * ID of the locked user that this activity blocks access to.
+     * Contains a {@link TaskDescription} for the activity being covered.
      */
-    @UserIdInt
-    private int mUserId;
-
+    static final String EXTRA_TASK_DESCRIPTION =
+            "com.android.systemui.keyguard.extra.TASK_DESCRIPTION";
+  
     /**
-     * {@see KeyguardManager}
+     * Cached keyguard manager instance populated by {@link #getKeyguardManager}.
+     * @see KeyguardManager
      */
     private KeyguardManager mKgm;
 
-    /**
-     * {@see DevicePolicyManager}
-     */
-    private DevicePolicyManager mDpm;
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
-        mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
-        mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
-
-        final IntentFilter lockFilter = new IntentFilter();
-        lockFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
-        registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, lockFilter,
-                /* permission */ null, /* scheduler */ null);
+        registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL,
+                new IntentFilter(Intent.ACTION_DEVICE_LOCKED_CHANGED), /* permission */ null,
+                /* scheduler */ null);
 
         // Once the receiver is registered, check whether anything happened between now and the time
         // when this activity was launched. If it did and the user is unlocked now, just quit.
-        if (!mKgm.isDeviceLocked(mUserId)) {
+        if (!getKeyguardManager().isDeviceLocked(getTargetUserId())) {
             finish();
             return;
         }
 
-        // Get the organization color; this is a 24-bit integer provided by a DPC, guaranteed to
-        // be completely opaque.
-        final @ColorInt int color = mDpm.getOrganizationColorForUser(mUserId);
-
         // Draw captions overlaid on the content view, so the whole window is one solid color.
         setOverlayWithDecorCaptionEnabled(true);
 
         // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with
         // redaction switched on.
         final View blankView = new View(this);
-        blankView.setBackgroundColor(color);
+        blankView.setBackgroundColor(getPrimaryColor());
         setContentView(blankView);
     }
 
@@ -127,26 +114,28 @@
 
     @Override
     public void setTaskDescription(TaskDescription taskDescription) {
-        // Use the previous activity's task description.
+        // Leave unset so we use the previous activity's task description.
     }
 
     private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, mUserId);
-            if (userId == mUserId && !mKgm.isDeviceLocked(mUserId)) {
+            final int targetUserId = getTargetUserId();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, targetUserId);
+            if (userId == targetUserId && !getKeyguardManager().isDeviceLocked(targetUserId)) {
                 finish();
             }
         }
     };
 
     private void showConfirmCredentialActivity() {
-        if (isFinishing() || !mKgm.isDeviceLocked(mUserId)) {
+        if (isFinishing() || !getKeyguardManager().isDeviceLocked(getTargetUserId())) {
             // Don't show the confirm credentials screen if we are already unlocked / unlocking.
             return;
         }
 
-        final Intent credential = mKgm.createConfirmDeviceCredentialIntent(null, null, mUserId);
+        final Intent credential = getKeyguardManager()
+                .createConfirmDeviceCredentialIntent(null, null, getTargetUserId());
         if (credential == null) {
             return;
         }
@@ -181,4 +170,32 @@
         final View view = getWindow().getDecorView();
         return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
     }
+
+    private KeyguardManager getKeyguardManager() {
+        if (mKgm == null) {
+            mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+        }
+        return mKgm;
+    }
+
+    @VisibleForTesting
+    @UserIdInt
+    final int getTargetUserId() {
+        return getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
+    }
+
+    @VisibleForTesting
+    @ColorInt
+    final int getPrimaryColor() {
+        final TaskDescription taskDescription = (TaskDescription)
+                getIntent().getExtra(EXTRA_TASK_DESCRIPTION);
+        if (taskDescription != null && Color.alpha(taskDescription.getPrimaryColor()) == 255) {
+            return taskDescription.getPrimaryColor();
+        } else {
+            // No task description. Use an organization color set by the policy controller.
+            final DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
+                    getSystemService(Context.DEVICE_POLICY_SERVICE);
+            return devicePolicyManager.getOrganizationColorForUser(getTargetUserId());
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index e6483f6..a49c482 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -31,17 +31,21 @@
 
 public class WorkLockActivityController {
     private final Context mContext;
+    final SystemServicesProxy mSsp;
 
     public WorkLockActivityController(Context context) {
         mContext = context;
+        mSsp = SystemServicesProxy.getInstance(context);
+
         EventBus.getDefault().register(this);
-        SystemServicesProxy.getInstance(context).registerTaskStackListener(mLockListener);
+        mSsp.registerTaskStackListener(mLockListener);
     }
 
     private void startWorkChallengeInTask(int taskId, int userId) {
         Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
                 .setComponent(new ComponentName(mContext, WorkLockActivity.class))
                 .putExtra(Intent.EXTRA_USER_ID, userId)
+                .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, mSsp.getTaskDescription(taskId))
                 .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
                         | Intent.FLAG_ACTIVITY_CLEAR_TOP);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index fff8305..8227f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -35,8 +35,8 @@
 import android.widget.TextView;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.BatteryInfo;
+import com.android.settingslib.graph.BatteryMeterDrawableBase;
 import com.android.settingslib.graph.UsageView;
-import com.android.systemui.BatteryMeterDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
@@ -155,8 +155,10 @@
 
     private final class BatteryDetail implements DetailAdapter, OnClickListener,
             OnAttachStateChangeListener {
-        private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(),
-                mHost.getContext().getColor(R.color.batterymeter_frame_color));
+        private final BatteryMeterDrawableBase mDrawable
+                = new BatteryMeterDrawableBase(
+                        mHost.getContext(),
+                        mHost.getContext().getColor(R.color.batterymeter_frame_color));
         private View mCurrentView;
 
         @Override
@@ -195,8 +197,9 @@
             if (mCurrentView == null) {
                 return;
             }
-            mDrawable.onBatteryLevelChanged(100, false, false);
-            mDrawable.onPowerSaveChanged(true);
+            mDrawable.setBatteryLevel(100);
+            mDrawable.setPluggedIn(false);
+            mDrawable.setPowerSave(true);
             mDrawable.setShowPercent(false);
             ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
             Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index ae4d6c9..7e6bb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -61,7 +61,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_SYNC_SETTINGS);
+        return new Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 55491b2..8de4e58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -130,6 +130,7 @@
                 launchOpts.numVisibleTaskThumbnails = 2;
                 launchOpts.onlyLoadForCache = true;
                 launchOpts.onlyLoadPausedActivities = true;
+                launchOpts.loadThumbnails = !ActivityManager.ENABLE_TASK_SNAPSHOTS;
                 loader.loadTasks(mContext, plan, launchOpts);
             }
         }
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 49074a6..eae1b81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -868,6 +868,14 @@
         return null;
     }
 
+    public ActivityManager.TaskDescription getTaskDescription(int taskId) {
+        try {
+            return mIam.getTaskDescription(taskId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
     /**
      * Returns the given icon for a user, badging if necessary.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 0160eb7..40aad45 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,6 +20,8 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
@@ -1496,11 +1498,6 @@
         // Remove the task from the ignored set
         removeIgnoreTask(removedTask);
 
-        // Resize the grid layout task view focus frame
-        if (mTaskViewFocusFrame != null) {
-            mTaskViewFocusFrame.resize();
-        }
-
         // If requested, relayout with the given animation
         if (animation != null) {
             updateLayoutAlgorithm(true /* boundScroll */);
@@ -1838,6 +1835,17 @@
         announceForAccessibility(getContext().getString(
                 R.string.accessibility_recents_item_dismissed, event.task.title));
 
+        if (useGridLayout() && event.animation != null) {
+            event.animation.setListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd(Animator animator) {
+                    if (mTaskViewFocusFrame != null) {
+                        // Resize the grid layout task view focus frame
+                        mTaskViewFocusFrame.resize();
+                    }
+                }
+            });
+        }
+
         // Remove the task from the stack
         mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
         EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
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 9a52a7b..a5f7832 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -270,7 +270,6 @@
         return super.onInterceptTouchEvent(ev);
     }
 
-
     @Override
     protected void measureContents(int width, int height) {
         int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 4ac0f9e..02b0113 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -77,10 +77,11 @@
     @ViewDebug.ExportedProperty(category="recents")
     private float mDimAlpha;
     private Matrix mMatrix = new Matrix();
-    protected Paint mDrawPaint = new Paint();
-    private Paint mLockedPaint = new Paint();
+    private Paint mDrawPaint = new Paint();
+    protected Paint mLockedPaint = new Paint();
     protected Paint mBgFillPaint = new Paint();
     protected BitmapShader mBitmapShader;
+    protected boolean mUserLocked = false;
     private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
 
     // Clip the top of the thumbnail against the opaque header bar that overlaps this view
@@ -152,7 +153,7 @@
         int thumbnailHeight = Math.min(viewHeight,
                 (int) (mThumbnailRect.height() * mThumbnailScale));
 
-        if (mTask != null && mTask.isLocked) {
+        if (mUserLocked) {
             canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
                     mLockedPaint);
         } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
@@ -344,6 +345,17 @@
     }
 
     /**
+     * Returns the {@link Paint} used to draw a task screenshot, or {@link #mLockedPaint} if the
+     * thumbnail shouldn't be drawn because it belongs to a locked user.
+     */
+    protected Paint getDrawPaint() {
+        if (mUserLocked) {
+            return mLockedPaint;
+        }
+        return mDrawPaint;
+    }
+
+    /**
      * Binds the thumbnail view to the task.
      */
     void bindToTask(Task t, boolean disabledInSafeMode, int displayOrientation, Rect displayRect) {
@@ -354,7 +366,10 @@
         if (t.colorBackground != 0) {
             mBgFillPaint.setColor(t.colorBackground);
         }
-        mLockedPaint.setColor(t.colorPrimary);
+        if (t.colorPrimary != 0) {
+            mLockedPaint.setColor(t.colorPrimary);
+        }
+        mUserLocked = t.isLocked;
         EventBus.getDefault().register(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
index 2c3e42b..bcf4f17 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
@@ -28,7 +28,6 @@
 
     private Path mThumbnailOutline;
     private Path mRestBackgroundOutline;
-    private Path mFullBackgroundOutline;
     // True if either this view's size or thumbnail scale has changed and mThumbnailOutline should
     // be updated.
     private boolean mUpdateThumbnailOutline = true;
@@ -145,10 +144,7 @@
                         90, 90, false); // F
                 mRestBackgroundOutline.lineTo(l, t); // A
                 mRestBackgroundOutline.close();
-
             }
-        } else {
-            mFullBackgroundOutline = mThumbnailOutline;
         }
     }
 
@@ -167,7 +163,10 @@
             updateThumbnailOutline();
             mUpdateThumbnailOutline = false;
         }
-        if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+
+        if (mUserLocked) {
+            canvas.drawPath(mThumbnailOutline, mLockedPaint);
+        } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
             // Draw the background, there will be some small overdraw with the thumbnail
             if (thumbnailWidth < viewWidth) {
                 // Portrait thumbnail on a landscape task view
@@ -177,9 +176,9 @@
                 // Landscape thumbnail on a portrait task view
                 canvas.drawPath(mRestBackgroundOutline, mBgFillPaint);
             }
-            canvas.drawPath(mThumbnailOutline, mDrawPaint);
+            canvas.drawPath(mThumbnailOutline, getDrawPaint());
         } else {
-            canvas.drawPath(mFullBackgroundOutline, mBgFillPaint);
+            canvas.drawPath(mThumbnailOutline, mBgFillPaint);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 5366da1..995901b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -164,15 +164,19 @@
         }
     }
 
-    public void disable(int state1, int state2) {
+    public void disable(int state1, int state2, boolean animate) {
         synchronized (mLock) {
             mDisable1 = state1;
             mDisable2 = state2;
             mHandler.removeMessages(MSG_DISABLE);
-            mHandler.obtainMessage(MSG_DISABLE, state1, state2, null).sendToTarget();
+            mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate).sendToTarget();
         }
     }
 
+    public void disable(int state1, int state2) {
+        disable(state1, state2, true);
+    }
+
     public void animateExpandNotificationsPanel() {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_EXPAND_NOTIFICATIONS);
@@ -433,7 +437,7 @@
                 }
                 case MSG_DISABLE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).disable(msg.arg1, msg.arg2, true /* animate */);
+                        mCallbacks.get(i).disable(msg.arg1, msg.arg2, (Boolean) msg.obj);
                     }
                     break;
                 case MSG_EXPAND_NOTIFICATIONS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 2e9c7fd..3bbaf99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -76,6 +76,7 @@
     private int mNotificationMinHeightLegacy;
     private int mMaxHeadsUpHeightLegacy;
     private int mMaxHeadsUpHeight;
+    private int mMaxHeadsUpHeightIncreased;
     private int mNotificationMinHeight;
     private int mNotificationMinHeightLarge;
     private int mNotificationMaxHeight;
@@ -209,6 +210,9 @@
     private boolean mIsLowPriority;
     private boolean mIsColorized;
     private boolean mUseIncreasedCollapsedHeight;
+    private boolean mUseIncreasedHeadsUpHeight;
+    private float mTranslationWhenRemoved;
+    private boolean mWasChildInGroupWhenRemoved;
 
     @Override
     public boolean isGroupExpansionChanging() {
@@ -354,8 +358,14 @@
         boolean headsUpCustom = layout.getHeadsUpChild() != null &&
                 layout.getHeadsUpChild().getId()
                         != com.android.internal.R.id.status_bar_latest_event_content;
-        int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
-                : mMaxHeadsUpHeight;
+        int headsUpheight;
+        if (headsUpCustom && beforeN) {
+            headsUpheight = mMaxHeadsUpHeightLegacy;
+        } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
+            headsUpheight = mMaxHeadsUpHeightIncreased;
+        } else {
+            headsUpheight = mMaxHeadsUpHeight;
+        }
         layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
                 mNotificationAmbientHeight);
     }
@@ -828,10 +838,22 @@
 
     public void setRemoved() {
         mRemoved = true;
-
+        mTranslationWhenRemoved = getTranslationY();
+        mWasChildInGroupWhenRemoved = isChildInGroup();
+        if (isChildInGroup()) {
+            mTranslationWhenRemoved += getNotificationParent().getTranslationY();
+        }
         mPrivateLayout.setRemoved();
     }
 
+    public boolean wasChildInGroupWhenRemoved() {
+        return mWasChildInGroupWhenRemoved;
+    }
+
+    public float getTranslationWhenRemoved() {
+        return mTranslationWhenRemoved;
+    }
+
     public NotificationChildrenContainer getChildrenContainer() {
         return mChildrenContainer;
     }
@@ -991,6 +1013,10 @@
         mUseIncreasedCollapsedHeight = use;
     }
 
+    public void setUseIncreasedHeadsUpHeight(boolean use) {
+        mUseIncreasedHeadsUpHeight = use;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -1005,12 +1031,14 @@
         mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
         mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
         mNotificationMinHeightLarge = getFontScaledHeight(
-                R.dimen.notification_min_height_large);
+                R.dimen.notification_min_height_increased);
         mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
         mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
         mMaxHeadsUpHeightLegacy = getFontScaledHeight(
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
+        mMaxHeadsUpHeightIncreased = getFontScaledHeight(
+                R.dimen.notification_max_heads_up_height_increased);
         mIncreasedPaddingBetweenElements = getResources()
                 .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
         mIconTransformContentShiftNoIcon = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index bd5fb92..0ea56b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -381,12 +381,7 @@
                         if (appShortcuts != null) {
                             result.add(appShortcuts);
                         }
-                        synchronized (sLock) {
-                            // showKeyboardShortcutsDialog only if it has not been dismissed already
-                            if (sInstance != null) {
-                                showKeyboardShortcutsDialog(result);
-                            }
-                        }
+                        showKeyboardShortcutsDialog(result);
                     }
                 }, deviceId);
     }
@@ -585,7 +580,12 @@
         mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
         Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
         keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
-        mKeyboardShortcutsDialog.show();
+        synchronized (sLock) {
+            // showKeyboardShortcutsDialog only if it has not been dismissed already
+            if (sInstance != null) {
+                mKeyboardShortcutsDialog.show();
+            }
+        }
     }
 
     private void populateKeyboardShortcuts(LinearLayout keyboardShortcutsLayout,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index f73a5ea..81db429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -126,7 +126,8 @@
         }
 
         public boolean cacheContentViews(Context ctx, Notification updatedNotification,
-                boolean isLowPriority, boolean useIncreasedCollapsedView) {
+                boolean isLowPriority, boolean useIncreasedCollapsedView,
+                boolean useIncreasedHeadsUp) {
             boolean applyInPlace = false;
             if (updatedNotification != null) {
                 final Notification.Builder updatedNotificationBuilder
@@ -136,7 +137,7 @@
                 final RemoteViews newBigContentView = createBigContentView(
                         updatedNotificationBuilder, isLowPriority);
                 final RemoteViews newHeadsUpContentView =
-                        updatedNotificationBuilder.createHeadsUpContentView();
+                        updatedNotificationBuilder.createHeadsUpContentView(useIncreasedHeadsUp);
                 final RemoteViews newPublicNotification
                         = updatedNotificationBuilder.makePublicContentView();
                 final RemoteViews newAmbientNotification
@@ -165,7 +166,7 @@
                 cachedContentView = createContentView(builder, isLowPriority,
                         useIncreasedCollapsedView);
                 cachedBigContentView = createBigContentView(builder, isLowPriority);
-                cachedHeadsUpContentView = builder.createHeadsUpContentView();
+                cachedHeadsUpContentView = builder.createHeadsUpContentView(useIncreasedHeadsUp);
                 cachedPublicContentView = builder.makePublicContentView();
                 cachedAmbientContentView = builder.makeAmbientNotification();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 1c89e32..5353005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -224,9 +224,6 @@
         stack.push(viewRoot);
         while (!stack.isEmpty()) {
             View child = stack.pop();
-            if (child.getVisibility() == View.GONE) {
-                continue;
-            }
             Boolean containsView = (Boolean) child.getTag(TAG_CONTAINS_TRANSFORMED_VIEW);
             if (containsView == null) {
                 // This one is unhandled, let's add it to our list.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index d15ab10..c3f1cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -69,7 +69,8 @@
     public void transformViewFrom(TransformState otherState, float transformationAmount) {
         mTransformedView.animate().cancel();
         if (sameAs(otherState)) {
-            if (mTransformedView.getVisibility() == View.INVISIBLE) {
+            if (mTransformedView.getVisibility() == View.INVISIBLE
+                    || mTransformedView.getAlpha() != 1.0f) {
                 // We have the same content, lets show ourselves
                 mTransformedView.setAlpha(1.0f);
                 mTransformedView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index a4e5916..8dab069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -72,6 +72,9 @@
      * @param pulsing whether we are currently pulsing for ambient display.
      */
     public void setPulsing(boolean pulsing) {
+        if (mPulsing == pulsing) {
+            return;
+        }
         mPulsing = pulsing;
         updateReorderingAllowed();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index bc43663..ac13cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -712,6 +712,7 @@
     private NetworkController mNetworkController;
     private KeyguardMonitorImpl mKeyguardMonitor;
     private BatteryController mBatteryController;
+    private boolean mPanelExpanded;
     private LogMaker mStatusBarStateLog;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private NotificationIconAreaController mNotificationIconAreaController;
@@ -838,7 +839,7 @@
         createAndAddWindows();
 
         mSettingsObserver.onChange(false); // set up
-        disable(switches[0], switches[6], false /* animate */);
+        mCommandQueue.disable(switches[0], switches[6], false /* animate */);
         setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
                 fullscreenStackBounds, dockedStackBounds);
         topAppWindowChanged(switches[2] != 0);
@@ -2510,7 +2511,7 @@
      * This needs to be called if state used by {@link #adjustDisableFlags} changes.
      */
     public void recomputeDisableFlags(boolean animate) {
-        disable(mDisabledUnmodified1, mDisabledUnmodified2, animate);
+        mCommandQueue.disable(mDisabledUnmodified1, mDisabledUnmodified2, animate);
     }
 
     protected H createHandler() {
@@ -2679,6 +2680,7 @@
     }
 
     public void setPanelExpanded(boolean isExpanded) {
+        mPanelExpanded = isExpanded;
         mStatusBarWindowManager.setPanelExpanded(isExpanded);
         mVisualStabilityManager.setPanelExpanded(isExpanded);
         if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
@@ -5005,8 +5007,12 @@
                 @Override
                 public void onPulseStarted() {
                     callback.onPulseStarted();
-                    mStackScroller.setPulsing(true);
-                    mVisualStabilityManager.setPulsing(true);
+                    if (!mHeadsUpManager.getAllEntries().isEmpty()) {
+                        // Only pulse the stack scroller if there's actually something to show.
+                        // Otherwise just show the always-on screen.
+                        mStackScroller.setPulsing(true);
+                        mVisualStabilityManager.setPulsing(true);
+                    }
                 }
 
                 @Override
@@ -6090,8 +6096,10 @@
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
         boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
                 mNotificationData.getImportance(sbn.getKey()));
+        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
         try {
-            entry.cacheContentViews(mContext, null, isLowPriority, useIncreasedCollapsedHeight);
+            entry.cacheContentViews(mContext, null, isLowPriority, useIncreasedCollapsedHeight,
+                    useIncreasedHeadsUp);
         } catch (RuntimeException e) {
             Log.e(TAG, "Unable to get notification remote views", e);
             return false;
@@ -6262,6 +6270,7 @@
         }
         row.setUserLocked(userLocked);
         row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         row.onNotificationUpdated(entry);
         return true;
     }
@@ -6756,10 +6765,13 @@
         boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(notification,
                 mNotificationData.getImportance(notification.getKey()));
         entry.row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
+        entry.row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         boolean applyInPlace;
         try {
             applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(),
-                    mNotificationData.isAmbient(key), useIncreasedCollapsedHeight);
+                    mNotificationData.isAmbient(key), useIncreasedCollapsedHeight,
+                    useIncreasedHeadsUp);
         } catch (RuntimeException e) {
             Log.e(TAG, "Unable to get notification remote views", e);
             applyInPlace = false;
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 dd4e876..67cc5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -88,6 +88,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -1836,12 +1837,29 @@
      * @return The first child which has visibility unequal to GONE which is currently below the
      *         given translationY or equal to it.
      */
-    private View getFirstChildBelowTranlsationY(float translationY) {
+    private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) {
+            if (child.getVisibility() == View.GONE) {
+                continue;
+            }
+            float rowTranslation = child.getTranslationY();
+            if (rowTranslation >= translationY) {
                 return child;
+            } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+                    List<ExpandableNotificationRow> notificationChildren =
+                            row.getNotificationChildren();
+                    for (int childIndex = 0; childIndex < notificationChildren.size();
+                            childIndex++) {
+                        ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
+                        if (rowChild.getTranslationY() + rowTranslation >= translationY) {
+                            return rowChild;
+                        }
+                    }
+                }
             }
         }
         return null;
@@ -2500,7 +2518,7 @@
                     View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
                     nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
                             ? groupParentWhenDismissed.getTranslationY()
-                            : view.getTranslationY());
+                            : view.getTranslationY(), true /* ignoreChildren */);
                 }
                 if (nextView != null) {
                     nextView.requestAccessibilityFocus();
@@ -2940,7 +2958,17 @@
             AnimationEvent event = new AnimationEvent(child, animationType);
 
             // we need to know the view after this one
-            event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY());
+            float removedTranslation = child.getTranslationY();
+            boolean ignoreChildren = true;
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
+                    removedTranslation = row.getTranslationWhenRemoved();
+                    ignoreChildren = false;
+                }
+            }
+            event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
+                    ignoreChildren);
             mAnimationEvents.add(event);
             mSwipedOutViews.remove(child);
         }
@@ -4004,6 +4032,9 @@
     }
 
     public void setPulsing(boolean pulsing) {
+        if (mPulsing == pulsing) {
+            return;
+        }
         mPulsing = pulsing;
         updateNotificationAnimationStates();
         updateContentHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 55085e5..9893434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -389,10 +389,25 @@
                 // upwards by default
                 float translationDirection = -1.0f;
                 if (viewState != null) {
+                    float ownPosition = changingView.getTranslationY();
+                    if (changingView instanceof ExpandableNotificationRow
+                            && event.viewAfterChangingView instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow changingRow =
+                                (ExpandableNotificationRow) changingView;
+                        ExpandableNotificationRow nextRow =
+                                (ExpandableNotificationRow) event.viewAfterChangingView;
+                        if (changingRow.isRemoved()
+                                && changingRow.wasChildInGroupWhenRemoved()
+                                && !nextRow.isChildInGroup()) {
+                            // the next row isn't actually a child from a group! Let's
+                            // compare absolute positions!
+                            ownPosition = changingRow.getTranslationWhenRemoved();
+                        }
+                    }
                     // there was a view after this one, Approximate the distance the next child
                     // travelled
                     translationDirection = ((viewState.yTranslation
-                            - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 /
+                            - (ownPosition + actualHeight / 2.0f)) * 2 /
                             actualHeight);
                     translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 3058c0a..1df12ac 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -25,7 +25,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 
-import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING;
+import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING;
 
 public class BatteryPreference extends DropDownPreference implements TunerService.Tunable {
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 85be4d7..6a92b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -38,7 +38,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING;
+import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING;
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index cd465ac..ec5030b 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -120,8 +120,7 @@
         }
 
         CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
-        super.onCreate(savedInstanceState, target, title, null, rList,
-                true /* Set alwaysUseOption to true to enable "always use this app" checkbox. */ );
+        super.onCreate(savedInstanceState, target, title, null, rList, true);
 
         CheckBox alwaysUse = (CheckBox)findViewById(com.android.internal.R.id.alwaysUse);
         if (alwaysUse != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index ef743e3..ba9e60a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -20,6 +20,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.support.annotation.VisibleForTesting;
 
 import com.android.systemui.Dependency;
@@ -80,11 +81,15 @@
 
     public static class Service extends SystemUI {
 
+        // TODO(b/35345376): Turn this back on for debuggable builds after known leak fixed.
+        private static final boolean ENABLED = Build.IS_DEBUGGABLE
+                && SystemProperties.getBoolean("debug.enable_leak_reporting", false);
+
         private GarbageMonitor mGarbageMonitor;
 
         @Override
         public void start() {
-            if (!Build.IS_DEBUGGABLE) {
+            if (!ENABLED) {
                 return;
             }
             mGarbageMonitor = Dependency.get(GarbageMonitor.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
index 09808d4..6b47ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import com.android.settingslib.graph.BatteryMeterDrawableBase;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyFloat;
@@ -41,17 +42,18 @@
 public class BatteryMeterDrawableTest extends SysuiTestCase {
 
     private Resources mResources;
-    private BatteryMeterDrawable mBatteryMeter;
+    private BatteryMeterDrawableBase mBatteryMeter;
 
     @Before
     public void setUp() throws Exception {
         mResources = mContext.getResources();
-        mBatteryMeter = new BatteryMeterDrawable(mContext, 0);
+        mBatteryMeter = new BatteryMeterDrawableBase(mContext, 0);
     }
 
     @Test
     public void testDrawImageButNoTextIfPluggedIn() {
-        mBatteryMeter.onBatteryLevelChanged(0, true, true);
+        mBatteryMeter.setBatteryLevel(0);
+        mBatteryMeter.setPluggedIn(true);
         final Canvas canvas = mock(Canvas.class);
         mBatteryMeter.draw(canvas);
         verify(canvas, atLeastOnce()).drawPath(any(), any());
@@ -60,7 +62,8 @@
 
     @Test
     public void testDrawTextIfNotPluggedIn() {
-        mBatteryMeter.onBatteryLevelChanged(0, false, false);
+        mBatteryMeter.setBatteryLevel(0);
+        mBatteryMeter.setPluggedIn(false);
         final Canvas canvas = mock(Canvas.class);
         mBatteryMeter.draw(canvas);
         verify(canvas, times(1)).drawText(anyString(), anyFloat(), anyFloat(), any());
@@ -68,8 +71,9 @@
 
     @Test
     public void testDrawNoTextIfPowerSaveEnabled() {
-        mBatteryMeter.onBatteryLevelChanged(0, false, false);
-        mBatteryMeter.onPowerSaveChanged(true);
+        mBatteryMeter.setBatteryLevel(0);
+        mBatteryMeter.setPluggedIn(false);
+        mBatteryMeter.setPowerSave(true);
         final Canvas canvas = mock(Canvas.class);
         mBatteryMeter.draw(canvas);
         verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
@@ -79,7 +83,8 @@
     public void testDrawTextWarningAtCriticalLevel() {
         int criticalLevel = mResources.getInteger(
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
-        mBatteryMeter.onBatteryLevelChanged(criticalLevel, false, false);
+        mBatteryMeter.setBatteryLevel(criticalLevel);
+        mBatteryMeter.setPluggedIn(false);
         final Canvas canvas = mock(Canvas.class);
         mBatteryMeter.draw(canvas);
         String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
@@ -90,7 +95,8 @@
     public void testDrawTextNoWarningAboveCriticalLevel() {
         int criticalLevel = mResources.getInteger(
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
-        mBatteryMeter.onBatteryLevelChanged(criticalLevel + 1, false, false);
+        mBatteryMeter.setBatteryLevel(criticalLevel + 1);
+        mBatteryMeter.setPluggedIn(false);
         final Canvas canvas = mock(Canvas.class);
         mBatteryMeter.draw(canvas);
         String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java
new file mode 100644
index 0000000..9b868db
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard;
+
+import static android.app.ActivityManager.TaskDescription;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.annotation.ColorInt;
+import android.annotation.UserIdInt;
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Looper;
+
+import com.android.systemui.keyguard.WorkLockActivity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * runtest systemui -c com.android.systemui.keyguard.WorkLockActivityTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WorkLockActivityTest {
+    private static final @UserIdInt int USER_ID = 270;
+    private static final String TASK_LABEL = "task label";
+
+    private @Mock DevicePolicyManager mDevicePolicyManager;
+    private @Mock KeyguardManager mKeyguardManager;
+    private @Mock Context mContext;
+
+    private WorkLockActivity mActivity;
+
+    private static class WorkLockActivityTestable extends WorkLockActivity {
+        WorkLockActivityTestable(Context baseContext) {
+            super();
+            attachBaseContext(baseContext);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getSystemService(eq(Context.DEVICE_POLICY_SERVICE)))
+                .thenReturn(mDevicePolicyManager);
+        when(mContext.getSystemService(eq(Context.KEYGUARD_SERVICE)))
+                .thenReturn(mKeyguardManager);
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        mActivity = new WorkLockActivityTestable(mContext);
+    }
+
+    @Test
+    public void testBackgroundAlwaysOpaque() throws Exception {
+        final @ColorInt int orgColor = Color.rgb(250, 199, 67);
+        when(mDevicePolicyManager.getOrganizationColorForUser(eq(USER_ID))).thenReturn(orgColor);
+
+        final @ColorInt int opaqueColor= Color.rgb(164, 198, 57);
+        final @ColorInt int transparentColor = Color.argb(0, 0, 0, 0);
+        TaskDescription opaque = new TaskDescription(null, null, opaqueColor);
+        TaskDescription transparent = new TaskDescription(null, null, transparentColor);
+
+        // When a task description is provided with a suitable (opaque) primaryColor, it should be
+        // used as the scrim's background color.
+        mActivity.setIntent(new Intent()
+                .putExtra(Intent.EXTRA_USER_ID, USER_ID)
+                .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, opaque));
+        assertEquals(opaqueColor, mActivity.getPrimaryColor());
+
+        // When a task description is provided but has no primaryColor / the primaryColor is
+        // transparent, the organization color should be used instead.
+        mActivity.setIntent(new Intent()
+                .putExtra(Intent.EXTRA_USER_ID, USER_ID)
+                .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, transparent));
+        assertEquals(orgColor, mActivity.getPrimaryColor());
+
+        // When no task description is provided at all, it should be treated like a transparent
+        // description and the organization color shown instead.
+        mActivity.setIntent(new Intent()
+                .putExtra(Intent.EXTRA_USER_ID, USER_ID));
+        assertEquals(orgColor, mActivity.getPrimaryColor());
+    }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index e4b53cb..653e80a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3391,6 +3391,12 @@
     // OPEN: Carrier demo mode password dialog
     CARRIER_DEMO_MODE_PASSWORD = 828;
 
+    // ACTION: Create a Settings shortcut item.
+    ACTION_SETTINGS_CREATE_SHORTCUT = 829;
+
+    // ACTION: A tile in Settings information architecture is clicked
+    ACTION_SETTINGS_TILE_CLICK = 830;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
index ed0d234..64c6abd 100644
--- a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
+++ b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
@@ -126,13 +126,13 @@
             int oldLeft, int oldTop, int oldRight, int oldBottom) {
         if (view == mWindowSizeListenerView) {
             if (DEBUG) Slog.d(TAG, "onLayoutChange() for mWindowSizeListenerView");
-            // mWindowSizeListenerView layout changed, get the size of the display bounds and update
+            // mWindowSizeListenerView layout changed, get the size of the display bounds and updateLocked
             // the window.
             final Rect displayBounds = new Rect();
             view.getBoundsOnScreen(displayBounds);
             updateDisplayBounds(displayBounds);
         } else if (view == mContentView) {
-            // mContentView layout changed, update the window in case its height changed.
+            // mContentView layout changed, updateLocked the window in case its height changed.
             if (DEBUG) Slog.d(TAG, "onLayoutChange() for mContentView");
             updateHeight();
         }
@@ -159,7 +159,7 @@
                 MeasureSpec.makeMeasureSpec(displayBounds.height(), MeasureSpec.AT_MOST));
         int height = mContentView.getMeasuredHeight();
         if (height != mLastHeight) {
-            if (DEBUG) Slog.d(TAG, "update height=" + height);
+            if (DEBUG) Slog.d(TAG, "updateLocked height=" + height);
             mLastHeight = height;
             update(height, mLastBounds, displayBounds);
             return true;
@@ -170,7 +170,7 @@
 
     private void updateBounds(Rect bounds) {
         if (!bounds.equals(mLastBounds)) {
-            if (DEBUG) Slog.d(TAG, "update bounds=" + bounds);
+            if (DEBUG) Slog.d(TAG, "updateLocked bounds=" + bounds);
             mLastBounds = bounds;
 
             update(mLastHeight, bounds, mLastDisplayBounds);
@@ -179,7 +179,7 @@
 
     private void updateDisplayBounds(Rect displayBounds) {
         if (!displayBounds.equals(mLastDisplayBounds)) {
-            if (DEBUG) Slog.d(TAG, "update displayBounds=" + displayBounds);
+            if (DEBUG) Slog.d(TAG, "updateLocked displayBounds=" + displayBounds);
             mLastDisplayBounds = displayBounds;
 
             if (!updateHeight()) {
@@ -195,7 +195,7 @@
             return;
         }
 
-        if (DEBUG) Slog.d(TAG, "update height=" + height + ", bounds=" + bounds
+        if (DEBUG) Slog.d(TAG, "updateLocked height=" + height + ", bounds=" + bounds
                 + ", displayBounds=" + displayBounds);
 
         final LayoutParams params = createWindowLayoutParams(mAppToken,
@@ -220,7 +220,7 @@
      * the bounds is preferred, if it fits. Otherwise, anchor the window on the side with more
      * space.
      *
-     * @param params the params to update
+     * @param params the params to updateLocked
      * @param height the requested height of the window
      * @param minMargin the minimum margin between the window and the display bounds
      * @param bounds the region the window should be anchored to
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index c16a51c..3257812 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -22,14 +22,14 @@
 import static com.android.server.autofill.Helper.VERBOSE;
 
 import android.Manifest;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
-import android.content.ComponentName;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -37,14 +37,11 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.autofill.IAutoFillManagerService;
-import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.Slog;
@@ -52,11 +49,12 @@
 import android.view.autofill.AutoFillId;
 import android.view.autofill.AutoFillValue;
 
+import android.view.autofill.IAutoFillManager;
+import android.view.autofill.IAutoFillManagerClient;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.IResultReceiver;
-import com.android.internal.os.SomeArgs;
+import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
@@ -68,21 +66,15 @@
 /**
  * Entry point service for auto-fill management.
  *
- * <p>This service provides the {@link IAutoFillManagerService} implementation and keeps a list of
+ * <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of
  * {@link AutoFillManagerServiceImpl} per user; the real work is done by
  * {@link AutoFillManagerServiceImpl} itself.
  */
+// TODO(b/33197203): Handle removing of packages
 public final class AutoFillManagerService extends SystemService {
 
     private static final String TAG = "AutoFillManagerService";
 
-    private static final int MSG_START_SESSION = 1;
-    private static final int MSG_UPDATE_SESSION = 2;
-    private static final int MSG_FINISH_SESSION = 3;
-    private static final int MSG_REQUEST_SAVE_FOR_USER = 4;
-    private static final int MSG_LIST_SESSIONS = 5;
-    private static final int MSG_RESET = 6;
-
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
     private final Context mContext;
@@ -90,48 +82,6 @@
 
     private final Object mLock = new Object();
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        switch (msg.what) {
-            case MSG_START_SESSION: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final int userId = msg.arg1;
-                final IBinder activityToken = (IBinder) args.arg1;
-                final IBinder appCallback = (IBinder) args.arg2;
-                final AutoFillId autoFillId = (AutoFillId) args.arg3;
-                final Rect bounds = (Rect) args.arg4;
-                final AutoFillValue value = (AutoFillValue) args.arg5;
-                handleStartSession(userId, activityToken, appCallback, autoFillId, bounds, value);
-                return;
-            } case MSG_FINISH_SESSION: {
-                handleFinishSession(msg.arg1, (IBinder) msg.obj);
-                return;
-            } case MSG_REQUEST_SAVE_FOR_USER: {
-                handleSaveForUser(msg.arg1);
-                return;
-            } case MSG_UPDATE_SESSION: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final IBinder activityToken = (IBinder) args.arg1;
-                final AutoFillId autoFillId = (AutoFillId) args.arg2;
-                final Rect bounds = (Rect) args.arg3;
-                final AutoFillValue value = (AutoFillValue) args.arg4;
-                final int userId = args.argi5;
-                final int flags = args.argi6;
-                handleUpdateSession(userId, activityToken, autoFillId, bounds, value, flags);
-                return;
-            } case MSG_LIST_SESSIONS: {
-                handleListForUser(msg.arg1, (IResultReceiver) msg.obj);
-                return;
-            } case MSG_RESET: {
-                handleReset();
-                return;
-            } default: {
-                Slog.w(TAG, "Invalid message: " + msg);
-            }
-        }
-    };
-
-    private HandlerCaller mHandlerCaller;
-
     /**
      * Cache of {@link AutoFillManagerServiceImpl} per user id.
      * <p>
@@ -152,11 +102,26 @@
     // TODO(b/33197203): set a different max (or disable it) on low-memory devices.
     private final LocalLog mRequestsHistory = new LocalLog(100);
 
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                final String reason = intent.getStringExtra("reason");
+                if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
+                mUi.hideAll();
+            }
+        }
+    };
+
     public AutoFillManagerService(Context context) {
         super(context);
-        mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
         mContext = context;
         mUi = new AutoFillUI(mContext);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        mContext.registerReceiver(mBroadcastReceiver, filter, null,
+                FgThread.getHandler());
     }
 
     @Override
@@ -171,46 +136,30 @@
         }
     }
 
-    private AutoFillManagerServiceImpl newServiceForUser(int userId) {
-        ComponentName serviceComponent = null;
-        ServiceInfo serviceInfo = null;
-        final String componentName = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTO_FILL_SERVICE, userId);
-        if (!TextUtils.isEmpty(componentName)) {
-            try {
-                serviceComponent = ComponentName.unflattenFromString(componentName);
-                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0,
-                        userId);
-            } catch (RuntimeException | RemoteException e) {
-                Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
-                return null;
-            }
+    @Override
+    public void onUnlockUser(int userId) {
+        synchronized (mLock) {
+            updateCachedServiceLocked(userId);
         }
+    }
 
-        if (serviceInfo == null) {
-            return null;
+    @Override
+    public void onStopUser(int userId) {
+        synchronized (mLock) {
+            removeCachedServiceLocked(userId);
         }
-
-        try {
-            return new AutoFillManagerServiceImpl(mContext, mLock, mRequestsHistory,
-                    userId, serviceComponent, mUi);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.w(TAG, "Auto-fill service not found: " + serviceComponent, e);
-        }
-
-        return null;
     }
 
     /**
      * Gets the service instance for an user.
      *
-     * @return service instance or {@code null} if user does not have a service set.
+     * @return service instance.
      */
-    @Nullable
-    AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
+    @NonNull AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
         AutoFillManagerServiceImpl service = mServicesCache.get(userId);
         if (service == null) {
-            service = newServiceForUser(userId);
+            service = new AutoFillManagerServiceImpl(mContext, mLock,
+                    mRequestsHistory, userId, mUi);
             mServicesCache.put(userId, service);
         }
         return service;
@@ -220,81 +169,6 @@
     void requestSaveForUser(int userId) {
         Slog.i(TAG, "requestSaveForUser(): " + userId);
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageI(
-                MSG_REQUEST_SAVE_FOR_USER, userId));
-    }
-
-    // Called by Shell command.
-    void listSessions(int userId, IResultReceiver receiver) {
-        Slog.i(TAG, "listSessions() for userId " + userId);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        mHandlerCaller.sendMessage(
-                mHandlerCaller.obtainMessageIO(MSG_LIST_SESSIONS, userId, receiver));
-    }
-
-    // Called by Shell command.
-    void reset() {
-        Slog.i(TAG, "reset()");
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_RESET));
-    }
-
-    /**
-     * Removes a cached service for a given user.
-     */
-    void removeCachedServiceLocked(int userId) {
-        final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
-        if (service != null) {
-            mServicesCache.delete(userId);
-            service.destroyLocked();
-        }
-    }
-
-    private void handleStartSession(int userId, IBinder activityToken, IBinder appCallback,
-            AutoFillId autoFillId, Rect bounds, AutoFillValue value) {
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
-            if (service == null) {
-                return;
-            }
-           service.startSessionLocked(activityToken, appCallback, autoFillId, bounds, value);
-        }
-    }
-
-    private void handleFinishSession(int userId, IBinder activityToken) {
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
-            if (service == null) {
-                return;
-            }
-            service.finishSessionLocked(activityToken);
-        }
-    }
-
-    private void handleUpdateSession(int userId, IBinder activityToken, AutoFillId autoFillId,
-            Rect bounds, AutoFillValue value, int flags) {
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
-            if (service == null) {
-                return;
-            }
-
-            service.updateSessionLocked(activityToken, autoFillId, bounds, value, flags);
-        }
-    }
-
-    private IBinder getTopActivityForUser() {
-        final List<IBinder> topActivities = LocalServices
-                .getService(ActivityManagerInternal.class).getTopVisibleActivities();
-        if (DEBUG) Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
-        if (topActivities.isEmpty()) {
-            Slog.w(TAG, "Could not get top activity");
-            return null;
-        }
-        return topActivities.get(0);
-    }
-
-    private void handleSaveForUser(int userId) {
         final IBinder activityToken = getTopActivityForUser();
         if (activityToken != null) {
             synchronized (mLock) {
@@ -309,7 +183,10 @@
         }
     }
 
-    private void handleListForUser(int userId, IResultReceiver receiver) {
+    // Called by Shell command.
+    void listSessions(int userId, IResultReceiver receiver) {
+        Slog.i(TAG, "listSessions() for userId " + userId);
+        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         final Bundle resultData = new Bundle();
         final ArrayList<String> sessions = new ArrayList<>();
 
@@ -332,7 +209,10 @@
         }
     }
 
-    private void handleReset() {
+    // Called by Shell command.
+    void reset() {
+        Slog.i(TAG, "reset()");
+        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         synchronized (mLock) {
             final int size = mServicesCache.size();
             for (int i = 0; i < size; i++) {
@@ -342,48 +222,98 @@
         }
     }
 
-    final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
+    /**
+     * Removes a cached service for a given user.
+     */
+    private void removeCachedServiceLocked(int userId) {
+        final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+        if (service != null) {
+            mServicesCache.delete(userId);
+            service.destroyLocked();
+        }
+    }
+
+    /**
+     * Updates a cached service for a given user.
+     */
+    private void updateCachedServiceLocked(int userId) {
+        AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+        if (service != null) {
+            service.updateLocked();
+        }
+    }
+
+    private IBinder getTopActivityForUser() {
+        final List<IBinder> topActivities = LocalServices
+                .getService(ActivityManagerInternal.class).getTopVisibleActivities();
+        if (DEBUG) Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
+        if (topActivities.isEmpty()) {
+            Slog.w(TAG, "Could not get top activity");
+            return null;
+        }
+        return topActivities.get(0);
+    }
+
+    final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
+        @Override
+        public boolean addClient(IAutoFillManagerClient client, int userId) {
+            synchronized (mLock) {
+                return getServiceForUserLocked(userId).addClientLocked(client);
+            }
+        }
+
+        @Override
+        public void setAuthenticationResult(Bundle data, IBinder activityToken, int userId) {
+            synchronized (mLock) {
+                final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+                service.setAuthenticationResultLocked(data, activityToken);
+            }
+        }
 
         @Override
         public void startSession(IBinder activityToken, IBinder appCallback, AutoFillId autoFillId,
-                Rect bounds, AutoFillValue value) throws RemoteException {
+                Rect bounds, AutoFillValue value, int userId) {
             // TODO(b/33197203): make sure it's called by resumed / focused activity
 
-            final int userId = UserHandle.getCallingUserId();
             if (VERBOSE) {
                 Slog.v(TAG, "startSession: autoFillId=" + autoFillId + ", bounds=" + bounds
                         + ", value=" + value);
             }
 
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = activityToken;
-            args.arg2 = appCallback;
-            args.arg3 = autoFillId;
-            args.arg4 = bounds;
-            args.arg5 = value;
-
-            mHandlerCaller.sendMessage(mHandlerCaller.getHandler().obtainMessage(MSG_START_SESSION,
-                    userId, 0, args));
+            synchronized (mLock) {
+                final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+                service.startSessionLocked(activityToken, appCallback, autoFillId, bounds, value);
+            }
         }
 
         @Override
         public void updateSession(IBinder activityToken, AutoFillId id, Rect bounds,
-                AutoFillValue value, int flags) throws RemoteException {
+                AutoFillValue value, int flags, int userId) {
             if (DEBUG) {
                 Slog.d(TAG, "updateSession: flags=" + flags + ", autoFillId=" + id
                         + ", bounds=" + bounds + ", value=" + value);
             }
 
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOOII(MSG_UPDATE_SESSION,
-                    activityToken, id, bounds, value, UserHandle.getCallingUserId(), flags));
+            synchronized (mLock) {
+                final AutoFillManagerServiceImpl service = mServicesCache.get(
+                        UserHandle.getCallingUserId());
+                if (service != null) {
+                    service.updateSessionLocked(activityToken, id, bounds, value, flags);
+                }
+            }
         }
 
         @Override
-        public void finishSession(IBinder activityToken) throws RemoteException {
+        public void finishSession(IBinder activityToken, int userId) {
             if (VERBOSE) Slog.v(TAG, "finishSession(): " + activityToken);
 
-            mHandlerCaller.sendMessage(mHandlerCaller.getHandler().obtainMessage(MSG_FINISH_SESSION,
-                    UserHandle.getCallingUserId(), 0, activityToken));
+            synchronized (mLock) {
+                final AutoFillManagerServiceImpl service = mServicesCache.get(
+                        UserHandle.getCallingUserId());
+                if (service != null) {
+                    service.finishSessionLocked(activityToken);
+                }
+            }
         }
 
         @Override
@@ -433,7 +363,7 @@
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             synchronized (mLock) {
-                removeCachedServiceLocked(userId);
+                updateCachedServiceLocked(userId);
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 2891518..85bf5c2 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -31,43 +31,45 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.IActivityManager;
+import android.app.AppGlobals;
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.ICancellationSignal;
 import android.os.Looper;
+import android.os.Parcelable;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.service.autofill.AutoFillService;
 import android.service.autofill.AutoFillServiceInfo;
-import android.service.autofill.FillCallback;
-import android.service.autofill.IAutoFillAppCallback;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
-import android.service.autofill.IFillCallback;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillManager;
 import android.view.autofill.AutoFillValue;
-import android.view.autofill.Dataset;
-import android.view.autofill.FillResponse;
 
+import android.view.autofill.IAutoFillManagerClient;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.IResultReceiver;
-import com.android.server.FgThread;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -86,26 +88,14 @@
     private static final int MSG_SERVICE_SAVE = 1;
 
     private final int mUserId;
-    private final ComponentName mComponent;
-    private final String mComponentName;
     private final Context mContext;
-    private final IActivityManager mAm;
     private final Object mLock;
-    private final AutoFillServiceInfo mInfo;
     private final AutoFillUI mUi;
 
-    private final LocalLog mRequestsHistory;
+    private RemoteCallbackList<IAutoFillManagerClient> mClients;
+    private AutoFillServiceInfo mInfo;
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                final String reason = intent.getStringExtra("reason");
-                if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
-                mUi.hideAll();
-            }
-        }
-    };
+    private final LocalLog mRequestsHistory;
 
     private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
         switch (msg.what) {
@@ -119,6 +109,7 @@
 
     private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
             mHandlerCallback, true);
+
     /**
      * Cache of pending {@link Session}s, keyed by {@code activityToken}.
      *
@@ -139,7 +130,6 @@
             if (DEBUG) Slog.d(TAG, "resultCode on mAssistReceiver: " + resultCode);
 
             final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
-
             if (structure == null) {
                 Slog.w(TAG, "no assist structure for id " + resultCode);
                 return;
@@ -183,28 +173,78 @@
     };
 
     AutoFillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
-            int userId, ComponentName component, AutoFillUI ui)
-            throws PackageManager.NameNotFoundException {
+            int userId, AutoFillUI ui) {
         mContext = context;
         mLock = lock;
         mRequestsHistory = requestsHistory;
         mUserId = userId;
-        mComponent = component;
-        mComponentName = mComponent.flattenToShortString();
-        mAm = ActivityManager.getService();
         mUi = ui;
-        mInfo = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
+        updateLocked();
     }
 
+    CharSequence getServiceName() {
+        if (mInfo == null) {
+            return null;
+        }
+        final ComponentName serviceComponent = mInfo.getServiceInfo().getComponentName();
+        final String packageName = serviceComponent.getPackageName();
+
+        try {
+            final PackageManager pm = mContext.getPackageManager();
+            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return pm.getApplicationLabel(info);
+        } catch (Exception e) {
+            Slog.w(TAG, "Could not get label for " + packageName + ": " + e);
+            return packageName;
+        }
+    }
+
+    void updateLocked() {
+        ComponentName serviceComponent = null;
+        ServiceInfo serviceInfo = null;
+        final String componentName = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(), Settings.Secure.AUTO_FILL_SERVICE, mUserId);
+        if (!TextUtils.isEmpty(componentName)) {
+            try {
+                serviceComponent = ComponentName.unflattenFromString(componentName);
+                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                        0, mUserId);
+            } catch (RuntimeException | RemoteException e) {
+                Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
+                return;
+            }
+        }
+        try {
+            final boolean hadService = hasService();
+            if (serviceInfo != null) {
+                mInfo = new AutoFillServiceInfo(mContext.getPackageManager(),
+                        serviceComponent, mUserId);
+            } else {
+                mInfo = null;
+            }
+            if (hadService != hasService()) {
+                if (!hasService()) {
+                    final int sessionCount = mSessions.size();
+                    for (int i = sessionCount - 1; i >= 0; i--) {
+                        Session session = mSessions.valueAt(i);
+                        session.destroyLocked();
+                        mSessions.removeAt(i);
+                    }
+                }
+                sendStateToClients();
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
+        }
+    }
 
     /**
      * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
      */
     void requestSaveForUserLocked(IBinder activityToken) {
+        if (!hasService()) {
+            return;
+        }
         final Session session = mSessions.get(activityToken);
         if (session == null) {
             Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
@@ -214,9 +254,32 @@
         session.callSaveLocked();
     }
 
+    boolean addClientLocked(IAutoFillManagerClient client) {
+        if (mClients == null) {
+            mClients = new RemoteCallbackList<>();
+        }
+        mClients.register(client);
+        return hasService();
+    }
+
+    void setAuthenticationResultLocked(Bundle data, IBinder activityToken) {
+        if (!hasService()) {
+            return;
+        }
+        final Session session = mSessions.get(activityToken);
+        if (session != null) {
+            session.setAuthenticationResultLocked(data);
+        }
+    }
+
     void startSessionLocked(IBinder activityToken, IBinder appCallbackToken, AutoFillId autoFillId,
             Rect bounds, AutoFillValue value) {
-        final String historyItem = "s=" + mComponentName + " u=" + mUserId + " a=" + activityToken
+        if (!hasService()) {
+            return;
+        }
+
+        final String historyItem = "s=" + new ComponentName(mInfo.getServiceInfo().packageName,
+                mInfo.getServiceInfo().name) + " u=" + mUserId + " a=" + activityToken
                 + " i=" + autoFillId + " b=" + bounds + " v=" + value;
         mRequestsHistory.log(historyItem);
 
@@ -229,24 +292,23 @@
 
         final Session newSession = createSessionByTokenLocked(activityToken, appCallbackToken);
         newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION);
-        newSession.enableSessionLocked();
     }
 
     void finishSessionLocked(IBinder activityToken) {
-        if (DEBUG) Slog.d(TAG, "finishSessionLocked(): " + activityToken);
-        final Session session = mSessions.get(activityToken);
+        if (!hasService()) {
+            return;
+        }
 
+        final Session session = mSessions.get(activityToken);
         if (session == null) {
             Slog.w(TAG, "finishSessionLocked(): no session for " + activityToken);
             return;
         }
 
-        mUi.hideFillUi();
         session.showSaveLocked();
     }
 
     private Session createSessionByTokenLocked(IBinder activityToken, IBinder appCallbackToken) {
-
         final Session newSession = new Session(mContext, activityToken, appCallbackToken);
         mSessions.put(activityToken, newSession);
 
@@ -261,10 +323,16 @@
             // TODO(b/33197203): add MetricsLogger call
             final Bundle receiverExtras = new Bundle();
             receiverExtras.putBinder(EXTRA_ACTIVITY_TOKEN, activityToken);
-            if (!mAm.requestAutoFillData(mAssistReceiver, receiverExtras, activityToken)) {
-                // TODO(b/33197203): might need a way to warn user (perhaps a new method on
-                // AutoFillService).
-                Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (!ActivityManager.getService().requestAutoFillData(mAssistReceiver,
+                        receiverExtras, activityToken)) {
+                    // TODO(b/33197203): might need a way to warn user (perhaps a new method on
+                    // AutoFillService).
+                    Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         } catch (RemoteException e) {
             // Should not happen, it's a local call.
@@ -274,7 +342,6 @@
 
     void updateSessionLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds,
             AutoFillValue value, int flags) {
-
         // TODO(b/33197203): add MetricsLogger call
         final Session session = mSessions.get(activityToken);
         if (session == null) {
@@ -286,7 +353,6 @@
     }
 
     private void handleSessionSave(IBinder activityToken) {
-
         synchronized (mLock) {
             final Session session = mSessions.get(activityToken);
             if (session == null) {
@@ -301,7 +367,6 @@
     void destroyLocked() {
         if (VERBOSE) Slog.v(TAG, "destroyLocked()");
 
-        mContext.unregisterReceiver(mBroadcastReceiver);
         for (Session session : mSessions.values()) {
             session.destroyLocked();
         }
@@ -311,7 +376,8 @@
     void dumpLocked(String prefix, PrintWriter pw) {
         final String prefix2 = prefix + "  ";
 
-        pw.print(prefix); pw.println("Component:"); pw.println(mComponentName);
+        pw.print(prefix); pw.println("Component:"); pw.println(mInfo != null
+                ? mInfo.getServiceInfo().getComponentName() : null);
 
         if (VERBOSE) {
             // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
@@ -333,14 +399,44 @@
 
     void listSessionsLocked(ArrayList<String> output) {
         for (IBinder activityToken : mSessions.keySet()) {
-            output.add(mComponentName + ":" + activityToken);
+            output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
+                    : null) + ":" + activityToken);
         }
     }
 
+    private void sendStateToClients() {
+        final RemoteCallbackList<IAutoFillManagerClient> clients;
+        final int userClientCount;
+        synchronized (mLock) {
+            if (mClients == null) {
+                return;
+            }
+            clients = mClients;
+            userClientCount = clients.beginBroadcast();
+        }
+        try {
+            for (int i = 0; i < userClientCount; i++) {
+                IAutoFillManagerClient client = clients.getBroadcastItem(i);
+                try {
+                    client.setState(hasService());
+                } catch (RemoteException re) {
+                    /* ignore */
+                }
+            }
+        } finally {
+            clients.finishBroadcast();
+        }
+    }
+
+    private boolean hasService() {
+        return mInfo != null;
+    }
+
     @Override
     public String toString() {
         return "AutoFillManagerServiceImpl: [userId=" + mUserId
-                + ", component=" + mComponentName + "]";
+                + ", component=" + (mInfo != null
+                ? mInfo.getServiceInfo().getComponentName() : null) + "]";
     }
 
     /**
@@ -360,14 +456,20 @@
 
         final AutoFillId mId;
         private final Listener mListener;
-        // // TODO(b/33197203): does it really need a reference to the session's response?
-        private FillResponse mResponse;
+        // TODO(b/33197203): would not need a reference to response and session if it was an inner
+        // class of Session...
+        private final Session mSession;
+        // TODO(b/33197203): encapsulate access so it's not called by UI
+        FillResponse mResponse;
+        Intent mAuthIntent;
+
         private AutoFillValue mAutoFillValue;
         private Rect mBounds;
 
         private boolean mValueUpdated;
 
-        ViewState(AutoFillId id, Listener listener) {
+        ViewState(Session session, AutoFillId id, Listener listener) {
+            mSession = session;
             mId = id;
             mListener = listener;
         }
@@ -380,6 +482,18 @@
             maybeCallOnFillReady();
         }
 
+        /**
+         * Used when a {@link FillResponse} requires authentication to be unlocked.
+         */
+        void setResponse(FillResponse response, Intent authIntent) {
+            mAuthIntent = authIntent;
+            setResponse(response);
+        }
+
+        CharSequence getServiceName() {
+            return mSession.getServiceName();
+        }
+
         // TODO(b/33197203): need to refactor / rename / document this method to make it clear that
         // it can change  the value and update the UI; similarly, should replace code that
         // directly sets mAutoFilLValue to use encapsulation.
@@ -417,8 +531,8 @@
             pw.print(prefix); pw.print("value:" ); pw.println(mAutoFillValue);
             pw.print(prefix); pw.print("updated:" ); pw.println(mValueUpdated);
             pw.print(prefix); pw.print("bounds:" ); pw.println(mBounds);
+            pw.print(prefix); pw.print("authIntent:" ); pw.println(mAuthIntent);
         }
-
     }
 
     /**
@@ -448,7 +562,7 @@
         @Nullable
         private ViewState mCurrentViewState;
 
-        private final IAutoFillAppCallback mAppCallback;
+        private final IAutoFillManagerClient mClient;
 
         @GuardedBy("mLock")
         RemoteFillService mRemoteFillService;
@@ -471,23 +585,23 @@
         @GuardedBy("mLock")
         private AssistStructure mStructure;
 
-        private Session(Context context, IBinder activityToken, IBinder appCallback) {
-            mRemoteFillService = new RemoteFillService(context, mComponent, mUserId, this);
+        private Session(Context context, IBinder activityToken, IBinder client) {
+            mRemoteFillService = new RemoteFillService(context,
+                    mInfo.getServiceInfo().getComponentName(), mUserId, this);
             mActivityToken = activityToken;
 
-            mAppCallback = IAutoFillAppCallback.Stub.asInterface(appCallback);
+            mClient = IAutoFillManagerClient.Stub.asInterface(client);
             try {
-                appCallback.linkToDeath(() -> {
+                client.linkToDeath(() -> {
                     if (DEBUG) Slog.d(TAG, "app binder died");
 
                     removeSelf();
                 }, 0);
             } catch (RemoteException e) {
-                Slog.w(TAG, "linkToDeath() on mAppCallback failed: " + e);
+                Slog.w(TAG, "linkToDeath() on mClient failed: " + e);
             }
         }
 
-
         // FillServiceCallbacks
         @Override
         public void onFillRequestSuccess(FillResponse response) {
@@ -528,7 +642,7 @@
         // FillServiceCallbacks
         @Override
         public void authenticate(IntentSender intent, Intent fillInIntent) {
-            startAuthIntent(intent, fillInIntent);
+            startAuthentication(intent, fillInIntent);
         }
 
         // FillServiceCallbacks
@@ -550,6 +664,30 @@
                     .sendToTarget();
         }
 
+        public void setAuthenticationResultLocked(Bundle data) {
+            if (mCurrentResponse == null || data == null) {
+                removeSelf();
+            } else {
+                Parcelable result = data.getParcelable(
+                        AutoFillManager.EXTRA_AUTHENTICATION_RESULT);
+                if (result instanceof FillResponse) {
+                    mCurrentResponse = (FillResponse) result;
+                    processResponseLocked(mCurrentResponse);
+                } else if (result instanceof Dataset) {
+                    Dataset dataset = (Dataset) result;
+                    final int datasetIndex = Helper.indexOfDataset(
+                            dataset.getName(), mCurrentResponse);
+                    if (datasetIndex <= 0) {
+                        Slog.e(TAG, "Response for a dataset auth has"
+                                + " an invalid dataset result: " + dataset.getName());
+                    }
+                    mCurrentResponse.getDatasets().removeAt(datasetIndex);
+                    mCurrentResponse.getDatasets().add(dataset);
+                    autoFill(dataset);
+                }
+            }
+        }
+
         /**
          * Show the save UI, when session can be saved.
          */
@@ -661,11 +799,11 @@
 
             ViewState viewState = mViewStates.get(id);
             if (viewState == null) {
-                viewState = new ViewState(id, this);
+                viewState = new ViewState(this, id, this);
                 mViewStates.put(id, viewState);
             }
 
-            if ((flags & FLAG_START_SESSION) != 0 ) {
+            if ((flags & FLAG_START_SESSION) != 0) {
                 // View is triggering auto-fill.
                 mCurrentViewState = viewState;
                 viewState.update(value, bounds);
@@ -738,38 +876,24 @@
 
         private void processResponseLocked(FillResponse response) {
             if (DEBUG) Slog.d(TAG, "processResponseLocked(authRequired="
-                    + response.getAuthentication() +"):" + response);
+                    + response.getAuthentication() + "):" + response);
 
             // TODO(b/33197203): add MetricsLogger calls
 
+            if (mCurrentViewState == null) {
+                // TODO(b/33197203): temporary sanity check; should never happen
+                Slog.w(TAG, "processResponseLocked(): mCurrentResponse is null");
+                return;
+            }
+
             mCurrentResponse = response;
 
             if (mCurrentResponse.getAuthentication() != null) {
                 // Handle authentication.
-                final Intent fillInIntent = createAuthFillInIntent(response.getId(), mStructure,
-                        new Bundle(), new FillCallback(new IFillCallback.Stub() {
-                            @Override
-                            public void onCancellable(ICancellationSignal cancellation) {
-                                // TODO(b/33197203): Handle cancellation
-                            }
+                final Intent fillInIntent = createAuthFillInIntent(mStructure);
 
-                            @Override
-                            public void onSuccess(FillResponse response) {
-                                mCurrentResponse = createAuthenticatedResponse(
-                                        mCurrentResponse, response);
-                                processResponseLocked(mCurrentResponse);
-                            }
-
-                            @Override
-                            public void onFailure(CharSequence message) {
-                                getUiForShowing().showError(message);
-                                removeSelf();
-                            }
-                        }));
-
-                 getUiForShowing().showFillResponseAuthRequest(
-                         mCurrentResponse.getAuthentication(), fillInIntent);
-                 return;
+                mCurrentViewState.setResponse(mCurrentResponse, fillInIntent);
+                return;
             }
 
             final ArraySet<AutoFillId> savableIds = mCurrentResponse.getSavableIds();
@@ -782,10 +906,7 @@
                 return;
             }
 
-            // TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design
-            if (mCurrentViewState != null) {
-                mCurrentViewState.setResponse(mCurrentResponse);
-            }
+            mCurrentViewState.setResponse(mCurrentResponse);
         }
 
         void autoFill(Dataset dataset) {
@@ -797,48 +918,24 @@
                 }
 
                 // ...or handle authentication.
-                Intent fillInIntent = createAuthFillInIntent(dataset.getId(), mStructure,
-                        new Bundle(), new FillCallback(new IFillCallback.Stub() {
-                    @Override
-                    public void onCancellable(ICancellationSignal cancellation) {
-                        // TODO(b/33197203): Handle cancellation
-                    }
-
-                    @Override
-                    public void onSuccess(FillResponse response) {
-                        mCurrentResponse = createAuthenticatedResponse(
-                                mCurrentResponse, response);
-                        final Dataset augmentedDataset = Helper.findDatasetById(dataset.getId(),
-                                mCurrentResponse);
-                        if (augmentedDataset != null) {
-                            autoFill(augmentedDataset);
-                        }
-                    }
-
-                    @Override
-                    public void onFailure(CharSequence message) {
-                        getUiForShowing().showError(message);
-                        removeSelf();
-                    }
-                }));
-
-                startAuthIntent(dataset.getAuthentication(), fillInIntent);
+                Intent fillInIntent = createAuthFillInIntent(mStructure);
+                startAuthentication(dataset.getAuthentication(), fillInIntent);
             }
         }
 
-        private Intent createAuthFillInIntent(String itemId, AssistStructure structure,
-                Bundle extras, FillCallback fillCallback) {
+        CharSequence getServiceName() {
+            return AutoFillManagerServiceImpl.this.getServiceName();
+        }
+
+        private Intent createAuthFillInIntent(AssistStructure structure) {
             Intent fillInIntent = new Intent();
-            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ITEM_ID, itemId);
-            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ASSIST_STRUCTURE, structure);
-            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_EXTRAS, extras);
-            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_CALLBACK, fillCallback);
+            fillInIntent.putExtra(AutoFillManager.EXTRA_ASSIST_STRUCTURE, structure);
             return fillInIntent;
         }
 
-        private void startAuthIntent(IntentSender intent, Intent fillInIntent) {
+        private void startAuthentication(IntentSender intent, Intent fillInIntent) {
             try {
-                mAppCallback.startIntentSender(intent, fillInIntent);
+                mClient.authenticate(intent, fillInIntent);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error launching auth intent", e);
             }
@@ -874,7 +971,7 @@
                 try {
                     if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
 
-                    mAppCallback.autoFill(dataset);
+                    mClient.autoFill(dataset.getFieldIds(), dataset.getFieldValues());
                     mAutoFilledDataset = dataset;
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Error auto-filling activity: " + e);
@@ -882,16 +979,6 @@
             }
         }
 
-        void enableSessionLocked() {
-            if (DEBUG) Slog.d(TAG, "enableSessionLocked()");
-
-            try {
-                mAppCallback.enableSession();
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Error enabling session: " + e);
-            }
-        }
-
         private AutoFillUI getUiForShowing() {
             synchronized (mLock) {
                 mUi.setCallbackLocked(this, mActivityToken);
@@ -946,81 +1033,5 @@
                 mSessions.remove(mActivityToken);
             }
         }
-
-        /**
-         * Creates a response from the {@code original} and an {@code update} by
-         * replacing all items that needed authentication (response or datasets)
-         * with their updated version if the latter does not need authentication.
-         * New datasets that don't require auth are appended.
-         *
-         * @param original The original response requiring auth at some level.
-         * @param update An updated response with auth not needed anymore at some level.
-         * @return A new response with updated items where auth is not needed anymore.
-         */
-        // TODO(b/33197203) Unit test
-        FillResponse createAuthenticatedResponse(FillResponse original, FillResponse update) {
-            // Can update only if ids match
-            if (!original.getId().equals(update.getId())) {
-                return original;
-            }
-
-            // If the original required auth and the update doesn't, the update wins
-            // but only if none of the update's datasets requires authentication.
-            if (original.getAuthentication() != null && update.getAuthentication() == null) {
-                ArraySet<Dataset> updateDatasets = update.getDatasets();
-                final int udpateDatasetCount = updateDatasets.size();
-                for (int i = 0; i < udpateDatasetCount; i++) {
-                    Dataset updateDataset = updateDatasets.valueAt(i);
-                    if (updateDataset.getAuthentication() != null) {
-                        return original;
-                    }
-                }
-                return update;
-            }
-
-            // If no auth on response level we create a response that has all
-            // datasets from the original with the ones that required auth but
-            // not anymore updated and new ones not requiring auth appended.
-
-            // The update shouldn't require auth
-            if (update.getAuthentication() != null) {
-                return original;
-            }
-
-            final FillResponse.Builder builder = new FillResponse.Builder(original.getId());
-
-            // Update existing datasets
-            final ArraySet<Dataset> origDatasets = original.getDatasets();
-            final int origDatasetCount = origDatasets.size();
-            for (int i = 0; i < origDatasetCount; i++) {
-                Dataset origDataset = origDatasets.valueAt(i);
-                ArraySet<Dataset> updateDatasets = update.getDatasets();
-                final int updateDatasetCount = updateDatasets.size();
-                for (int j = 0; j < updateDatasetCount; j++) {
-                    Dataset updateDataset = updateDatasets.valueAt(j);
-                    if (origDataset.getId().equals(updateDataset.getId())) {
-                        // The update shouldn't require auth
-                        if (updateDataset.getAuthentication() == null) {
-                            origDataset = updateDataset;
-                            updateDatasets.removeAt(j);
-                        }
-                        break;
-                    }
-                }
-                builder.addDataset(origDataset);
-            }
-
-            // Add new datasets
-            final ArraySet<Dataset> updateDatasets = update.getDatasets();
-            final int updateDatasetCount = updateDatasets.size();
-            for (int i = 0; i < updateDatasetCount; i++) {
-                final Dataset updateDataset = updateDatasets.valueAt(i);
-                builder.addDataset(updateDataset);
-            }
-
-            // For now no extras and savable id updates.
-
-            return builder.build();
-        }
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 0763c74..e83dc1e 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -30,15 +30,13 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.service.autofill.Dataset;
 import android.util.ArraySet;
 import android.os.Looper;
 import android.text.format.DateUtils;
 import android.util.Slog;
-import android.view.autofill.Dataset;
-import android.view.autofill.FillResponse;
 import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.widget.Toast;
@@ -58,19 +56,15 @@
     private static final long SNACK_BAR_LIFETIME_MS = 30 * DateUtils.SECOND_IN_MILLIS;
     private static final int MSG_HIDE_SNACK_BAR = 1;
 
-    private static final String EXTRA_AUTH_INTENT_SENDER =
-            "com.android.server.autofill.extra.AUTH_INTENT_SENDER";
-    private static final String EXTRA_AUTH_FILL_IN_INTENT =
-            "com.android.server.autofill.extra.AUTH_FILL_IN_INTENT";
-
     private final Context mContext;
     private final WindowManager mWm;
 
     // TODO(b/33197203) Fix locking - some state requires lock and some not - requires refactoring
+    private final Object mLock = new Object();
 
     // Fill UI variables
     private AnchoredWindow mFillWindow;
-    private DatasetPicker mFillView;
+    private View mFillView;
     private ViewState mViewState;
 
     private AutoFillUiCallback mCallback;
@@ -158,62 +152,76 @@
 
         UiThread.getHandler().runWithScissors(() -> {
             hideSnackbarUiThread();
-            hideFillResponseAuthUiUiThread();
         }, 0);
 
-        if (datasets == null) {
+        if (datasets == null && viewState.mAuthIntent == null) {
             // TODO(b/33197203): shouldn't be called, but keeping the WTF for a while just to be
             // safe, otherwise it would crash system server...
             Slog.wtf(TAG, "showFillUI(): no dataset");
             return;
         }
 
+        // TODO(b/33197203): should not display UI after we launched an authentication intent, since
+        // we have no warranty the provider will call onFailure() if the authentication failed or
+        // user dismissed the auth window
+        // because if the service does not handle calling the callback,
+
+
         UiThread.getHandler().runWithScissors(() -> {
+            // The dataset picker is only shown when authentication is not required...
+            DatasetPicker datasetPicker = null;
+
             if (mViewState == null || !mViewState.mId.equals(viewState.mId)) {
                 hideFillUiUiThread();
 
                 mViewState = viewState;
+                if (viewState.mAuthIntent != null) {
+                    final CharSequence serviceName = viewState.getServiceName();
 
-                mFillView = new DatasetPicker(mContext, datasets,
-                        (dataset) -> {
-                            final AutoFillUiCallback callback;
-                            synchronized (mLock) {
-                                callback = mCallback;
-                            }
-                            if (callback != null) {
-                                callback.fill(dataset);
-                            } else {
-                                Slog.w(TAG, "null callback on showFillUi() for " + viewState.mId);
-                            }
-                            hideFillUi();
-                        });
+                    mFillView = new SignInPrompt(mContext, serviceName, (e) -> {
+                        final IntentSender intentSender = viewState.mResponse.getAuthentication();
+                        final AutoFillUiCallback callback;
+                        final Intent authIntent;
+                        synchronized (mLock) {
+                            callback = mCallback;
+                            authIntent = viewState.mAuthIntent;
+                            // Must reset the authentication intent so UI display the datasets after
+                            // the user authenticated.
+                            viewState.mAuthIntent = null;
+                        }
+                        if (callback != null) {
+                            callback.authenticate(intentSender, authIntent);
+                        } else {
+                            // TODO(b/33197203): need to figure out why it's null sometimes
+                            Slog.w(TAG, "no callback on showFillUi().auth for " + viewState.mId);
+                        }
+                    });
 
+                } else {
+                    mFillView = datasetPicker = new DatasetPicker(mContext, datasets,
+                            (dataset) -> {
+                                final AutoFillUiCallback callback;
+                                synchronized (mLock) {
+                                    callback = mCallback;
+                                }
+                                if (callback != null) {
+                                    callback.fill(dataset);
+                                } else {
+                                    // TODO(b/33197203): need to figure out why it's null sometimes
+                                    Slog.w(TAG, "no callback on showFillUi() for " + viewState.mId);
+                                }
+                                hideFillUiUiThread();
+                            });
+                }
                 mFillWindow = new AnchoredWindow(mWm, appToken, mFillView);
 
-                if (DEBUG) Slog.d(TAG, "showFillUi(): view changed");
+                if (DEBUG) Slog.d(TAG, "showFillUi(): view changed for: " + viewState.mId);
             }
-
-            if (DEBUG) Slog.d(TAG, "showFillUi(): bounds=" + bounds + ", filterText=" + filterText);
-            mFillView.update(filterText);
+            if (datasetPicker != null) {
+                datasetPicker.update(filterText);
+            }
             mFillWindow.show(bounds);
-        }, 0);
-    }
 
-    /**
-     * Shows an UI affordance indicating that user action is required before a {@link FillResponse}
-     * can be used.
-     *
-     * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to
-     * autofill" or "Tap to autofill", depending on the value of {@code usesFingerprint}.
-     */
-    void showFillResponseAuthRequest(IntentSender intent, Intent fillInIntent) {
-        if (!hasCallback()) {
-            return;
-        }
-        hideAll();
-        UiThread.getHandler().runWithScissors(() -> {
-            // TODO(b/33197203): proper implementation
-            showFillResponseAuthUiUiThread(intent, fillInIntent);
         }, 0);
     }
 
@@ -251,14 +259,12 @@
         UiThread.getHandler().runWithScissors(() -> {
             hideSnackbarUiThread();
             hideFillUiUiThread();
-            hideFillResponseAuthUiUiThread();
         }, 0);
     }
 
     void dump(PrintWriter pw) {
         pw.println("AufoFill UI");
         final String prefix = "  ";
-        pw.print(prefix); pw.print("sResultCode: "); pw.println(sResultCode);
         pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
         pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
         pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
@@ -311,103 +317,4 @@
         void fill(Dataset dataset);
         void save();
     }
-
-    /////////////////////////////////////////////////////////////////////////////////
-    // TODO(b/33197203): temporary code using a notification to request auto-fill. //
-    // Will be removed once UX decide the right way to present it to the user.     //
-    /////////////////////////////////////////////////////////////////////////////////
-
-    // TODO(b/33197203): remove from frameworks/base/core/res/AndroidManifest.xml once not used
-    private static final String NOTIFICATION_AUTO_FILL_INTENT =
-            "com.android.internal.autofill.action.REQUEST_AUTOFILL";
-
-    private BroadcastReceiver mNotificationReceiver;
-    private final Object mLock = new Object();
-
-    // Hack used to generate unique pending intents
-    static int sResultCode = 0;
-
-    private void ensureNotificationListener() {
-        synchronized (mLock) {
-            if (mNotificationReceiver == null) {
-                mNotificationReceiver = new NotificationReceiver();
-                mContext.registerReceiver(mNotificationReceiver,
-                        new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
-            }
-        }
-    }
-
-    final class NotificationReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final AutoFillUiCallback callback;
-            synchronized (mLock) {
-                callback = mCallback;
-            }
-            if (callback != null) {
-                IntentSender intentSender = intent.getParcelableExtra(EXTRA_AUTH_INTENT_SENDER);
-                Intent fillInIntent = intent.getParcelableExtra(EXTRA_AUTH_FILL_IN_INTENT);
-                callback.authenticate(intentSender, fillInIntent);
-            }
-            collapseStatusBar();
-        }
-    }
-
-    @android.annotation.UiThread
-    private void showFillResponseAuthUiUiThread(IntentSender intent, Intent fillInIntent) {
-        final String title = "AutoFill Authentication";
-        final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n");
-
-        final Intent authIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
-        authIntent.putExtra(EXTRA_AUTH_INTENT_SENDER, intent);
-        authIntent.putExtra(EXTRA_AUTH_FILL_IN_INTENT, fillInIntent);
-
-        final PendingIntent authPendingIntent = PendingIntent.getBroadcast(
-                mContext, ++sResultCode, authIntent, PendingIntent.FLAG_ONE_SHOT);
-
-        subTitle.append("Tap notification to launch its authentication UI.");
-
-        final Notification.Builder notification = newNotificationBuilder()
-                .setAutoCancel(true)
-                .setOngoing(false)
-                .setContentTitle(title)
-                .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString()))
-                .setContentIntent(authPendingIntent);
-
-        ensureNotificationListener();
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            NotificationManager.from(mContext).notify(0, notification.build());
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @android.annotation.UiThread
-    private void hideFillResponseAuthUiUiThread() {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            NotificationManager.from(mContext).cancel(0);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    private Notification.Builder newNotificationBuilder() {
-        return new Notification.Builder(mContext)
-                .setCategory(Notification.CATEGORY_SYSTEM)
-                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                .setLocalOnly(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
-    }
-
-    private void collapseStatusBar() {
-        final StatusBarManager sbm = (StatusBarManager) mContext.getSystemService("statusbar");
-        sbm.collapsePanels();
-    }
-    /////////////////////////////////////////
-    // End of temporary notification code. //
-    /////////////////////////////////////////
 }
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
index 9bee61a..a54cab9 100644
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
@@ -17,9 +17,9 @@
 
 import android.content.Context;
 import android.graphics.Color;
+import android.service.autofill.Dataset;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.view.autofill.Dataset;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 0f2bb60..48ae635 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -18,11 +18,11 @@
 
 import android.annotation.Nullable;
 import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
 import android.util.ArraySet;
 import android.view.autofill.AutoFillId;
 import android.view.autofill.AutoFillValue;
-import android.view.autofill.Dataset;
-import android.view.autofill.FillResponse;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -78,25 +78,25 @@
     }
 
     /**
-     * Finds a data set by id in a response.
+     * Finds the index of a data set given its name.
      *
-     * @param id The dataset id.
+     * @param name The dataset name.
      * @param response The response to search.
-     * @return The dataset if found or null.
+     * @return The index of dataset if found or -1.
      */
-    static Dataset findDatasetById(String id, FillResponse response) {
+    static int indexOfDataset(CharSequence name, FillResponse response) {
         ArraySet<Dataset> datasets = response.getDatasets();
         if (datasets == null || datasets.isEmpty()) {
-            return null;
+            return -1;
         }
         final int datasetCount = datasets.size();
         for (int i = 0; i < datasetCount; i++) {
             Dataset dataset = datasets.valueAt(i);
-            if (dataset.getId().equals(id)) {
-                return dataset;
+            if (dataset.getName().toString().equals(name.toString())) {
+                return i;
             }
         }
-        return null;
+        return -1;
     }
 
     private Helper() {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index c070f77..767fb46 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -31,12 +31,12 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.autofill.AutoFillService;
+import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
 import android.text.format.DateUtils;
 import android.util.Slog;
-import android.view.autofill.FillResponse;
 import com.android.internal.os.HandlerCaller;
 import com.android.server.FgThread;
 
diff --git a/services/autofill/java/com/android/server/autofill/SignInPrompt.java b/services/autofill/java/com/android/server/autofill/SignInPrompt.java
new file mode 100644
index 0000000..6d17acd
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/SignInPrompt.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * A view displaying the sign-in prompt for an auto-fill service.
+ */
+final class SignInPrompt extends Button {
+
+    SignInPrompt(Context context, CharSequence serviceName, View.OnClickListener listener) {
+        super(context);
+        // TODO(b/33197203): use strings.xml
+        final String text = serviceName != null
+                ? "Sign in to " + serviceName + " to autofill"
+                : "Sign in to autofill";
+
+        // TODO(b/33197203): polish UI / use better altenative than a button...
+        setText(text);
+        setOnClickListener(listener);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6bf0e8d..13e6ae0 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -599,15 +599,42 @@
     static final int OP_ACKNOWLEDGED = 1;
     static final int OP_TIMEOUT = -1;
 
-    class Operation {
-        public int state;
-        public BackupRestoreTask callback;
+    private static final int OP_TYPE_WAIT = 0;    // Waiting for BackupAgent.
+    private static final int OP_TYPE_BACKUP = 1;  // Backup operation in progress.
 
-        Operation(int initialState, BackupRestoreTask callbackObj) {
+    class Operation {
+        int state;
+        final BackupRestoreTask callback;
+        final int type;
+
+        Operation(int initialState, BackupRestoreTask callbackObj, int type) {
             state = initialState;
             callback = callbackObj;
+            this.type = type;
         }
     }
+
+    /**
+     * mCurrentOperations contains the list of currently active operations.
+     *
+     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+     * An operation wraps a BackupRestoreTask within it.
+     * It's the responsibility of this task to remove the operation from this array.
+     *
+     * A BackupRestore task gets notified of ack/timeout for the operation via
+     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+     * on the mCurrentOpLock. {@link BackupManagerService#waitUntilOperationComplete(int)} is
+     * used in various places to 'wait' for notifyAll and detect change of pending state of an
+     * operation. So typically, an operation will be removed from this array by:
+     *   - BackupRestoreTask#handleCancel and
+     *   - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+     *     these places because waitUntilOperationComplete relies on the operation being present to
+     *     determine its completion status.
+     *
+     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+     * cancel backup tasks.
+     */
+    @GuardedBy("mCurrentOpLock")
     final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
     final Object mCurrentOpLock = new Object();
     final Random mTokenGenerator = new Random();
@@ -967,7 +994,8 @@
 
             case MSG_TIMEOUT:
             {
-                handleTimeout(msg.arg1, msg.obj);
+                Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1));
+                handleCancel(msg.arg1, false);
                 break;
             }
 
@@ -2364,6 +2392,33 @@
         return BackupManager.SUCCESS;
     }
 
+    // Cancel all running backups.
+    public void cancelBackups(){
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "cancelBackups() called.");
+        }
+        final long oldToken = Binder.clearCallingIdentity();
+        try {
+            synchronized (mCurrentOpLock) {
+                for (int i = 0; i < mCurrentOperations.size(); i++) {
+                    Operation op = mCurrentOperations.valueAt(i);
+                    int token = mCurrentOperations.keyAt(i);
+                    if (op.type == OP_TYPE_BACKUP) {
+                        handleCancel(token, true /* cancelAll */);
+                    }
+                }
+            }
+
+            // We don't want the backup jobs to kick in any time soon.
+            // Reschedules them to run in the distant future.
+            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS);
+            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS);
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
     // -----
     // Interface and methods used by the asynchronous-with-timeout backup/restore operations
 
@@ -2375,20 +2430,34 @@
         void operationComplete(long result);
 
         // An operation that wanted a callback has timed out
-        void handleTimeout();
+        void handleCancel(boolean cancelAll);
     }
 
-    void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
+    void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
+            int operationType) {
         if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
                 + " interval=" + interval + " callback=" + callback);
         synchronized (mCurrentOpLock) {
-            mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
+            mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
 
             Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
             mBackupHandler.sendMessageDelayed(msg, interval);
         }
     }
 
+    private void removeOperation(int token) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
+        }
+        synchronized (mCurrentOpLock) {
+            if (mCurrentOperations.get(token) == null) {
+                Slog.w(TAG, "Duplicate remove for operation. token=" +
+                        Integer.toHexString(token));
+            }
+            mCurrentOperations.remove(token);
+        }
+    }
+
     // synchronous waiter case
     boolean waitUntilOperationComplete(int token) {
         if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
@@ -2405,9 +2474,14 @@
                     if (op.state == OP_PENDING) {
                         try {
                             mCurrentOpLock.wait();
-                        } catch (InterruptedException e) {}
+                        } catch (InterruptedException e) {
+                        }
                         // When the wait is notified we loop around and recheck the current state
                     } else {
+                        if (MORE_DEBUG) {
+                            Slog.d(TAG, "Unblocked waiting for operation token=" +
+                                    Integer.toHexString(token));
+                        }
                         // No longer pending; we're done
                         finalState = op.state;
                         break;
@@ -2416,33 +2490,36 @@
             }
         }
 
+        removeOperation(token);
         mBackupHandler.removeMessages(MSG_TIMEOUT);
         if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
                 + " complete: finalState=" + finalState);
         return finalState == OP_ACKNOWLEDGED;
     }
 
-    void handleTimeout(int token, Object obj) {
+    void handleCancel(int token, boolean cancelAll) {
         // Notify any synchronous waiters
         Operation op = null;
         synchronized (mCurrentOpLock) {
             op = mCurrentOperations.get(token);
             if (MORE_DEBUG) {
-                if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
+                if (op == null) Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
                         + " but no op found");
             }
             int state = (op != null) ? op.state : OP_TIMEOUT;
             if (state == OP_ACKNOWLEDGED) {
                 // The operation finished cleanly, so we have nothing more to do.
-                if (MORE_DEBUG) {
-                    Slog.v(TAG, "handleTimeout() after success; cleanup happens now");
+                if (DEBUG) {
+                    Slog.w(TAG, "Operation already got an ack." +
+                            "Should have been removed from mCurrentOperations.");
                 }
                 op = null;
                 mCurrentOperations.delete(token);
             } else if (state == OP_PENDING) {
-                if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
+                if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
                 op.state = OP_TIMEOUT;
-                // Leaves the object in place for later ack
+                // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
+                // called after we receive cancel here. We need this op's state there.
             }
             mCurrentOpLock.notifyAll();
         }
@@ -2450,9 +2527,9 @@
         // If there's a TimeoutHandler for this event, call it
         if (op != null && op.callback != null) {
             if (MORE_DEBUG) {
-                Slog.v(TAG, "   Invoking timeout on " + op.callback);
+                Slog.v(TAG, "   Invoking cancel on " + op.callback);
             }
-            op.callback.handleTimeout();
+            op.callback.handleCancel(cancelAll);
         }
     }
 
@@ -2464,9 +2541,36 @@
         FINAL
     }
 
+    /**
+     * This class handles the process of backing up a given list of key/value backup packages.
+     * Also takes in a list of pending dolly backups and kicks them off when key/value backups
+     * are done.
+     *
+     * Flow:
+     * If required, backup @pm@.
+     * For each pending key/value backup package:
+     *     - Bind to agent.
+     *     - Call agent.doBackup()
+     *     - Wait either for cancel/timeout or operationComplete() callback from the agent.
+     * Start task to perform dolly backups.
+     *
+     * There are three entry points into this class:
+     *     - execute() [Called from the handler thread]
+     *     - operationComplete(long result) [Called from the handler thread]
+     *     - handleCancel(boolean cancelAll) [Can be called from any thread]
+     * These methods synchronize on mCancelLock.
+     *
+     * Interaction with mCurrentOperations:
+     *     - An entry for this task is put into mCurrentOperations for the entire lifetime of the
+     *       task. This is useful to cancel the task if required.
+     *     - An ephemeral entry is put into mCurrentOperations each time we are waiting on for
+     *       response from a backup agent. This is used to plumb timeouts and completion callbacks.
+     */
     class PerformBackupTask implements BackupRestoreTask {
         private static final String TAG = "PerformBackupTask";
 
+        private final Object mCancelLock = new Object();
+
         IBackupTransport mTransport;
         ArrayList<BackupRequest> mQueue;
         ArrayList<BackupRequest> mOriginalQueue;
@@ -2477,6 +2581,10 @@
         IBackupObserver mObserver;
         IBackupManagerMonitor mMonitor;
 
+        private final PerformFullTransportBackupTask mFullBackupTask;
+        private final int mCurrentOpToken;
+        private volatile int mEphemeralOpToken;
+
         // carried information about the current in-flight operation
         IBackupAgent mAgentBinder;
         PackageInfo mCurrentPackage;
@@ -2491,6 +2599,8 @@
         final boolean mUserInitiated;
         final boolean mNonIncremental;
 
+        private volatile boolean mCancelAll;
+
         public PerformBackupTask(IBackupTransport transport, String dirName,
                 ArrayList<BackupRequest> queue, File journal, IBackupObserver observer,
                 IBackupManagerMonitor monitor, ArrayList<String> pendingFullBackups,
@@ -2505,33 +2615,63 @@
             mNonIncremental = nonIncremental;
 
             mStateDir = new File(mBaseStateDir, dirName);
+            mCurrentOpToken = generateToken();
 
             mCurrentState = BackupState.INITIAL;
             mFinished = false;
 
+            CountDownLatch latch = new CountDownLatch(1);
+            String[] fullBackups =
+                    mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
+            mFullBackupTask =
+                    new PerformFullTransportBackupTask(/*fullBackupRestoreObserver*/ null,
+                            fullBackups, /*updateSchedule*/ false, /*runningJob*/ null, latch,
+                            mObserver, mMonitor,mUserInitiated);
+
+            registerTask();
             addBackupTrace("STATE => INITIAL");
         }
 
+        /**
+         * Put this task in the repository of running tasks.
+         */
+        private void registerTask() {
+            synchronized (mCurrentOpLock) {
+                mCurrentOperations.put(mCurrentOpToken, new Operation(OP_PENDING, this,
+                        OP_TYPE_BACKUP));
+            }
+        }
+
+        /**
+         * Remove this task from repository of running tasks.
+         */
+        private void unregisterTask() {
+            removeOperation(mCurrentOpToken);
+        }
+
         // Main entry point: perform one chunk of work, updating the state as appropriate
         // and reposting the next chunk to the primary backup handler thread.
         @Override
+        @GuardedBy("mCancelLock")
         public void execute() {
-            switch (mCurrentState) {
-                case INITIAL:
-                    beginBackup();
-                    break;
+            synchronized (mCancelLock) {
+                switch (mCurrentState) {
+                    case INITIAL:
+                        beginBackup();
+                        break;
 
-                case RUNNING_QUEUE:
-                    invokeNextAgent();
-                    break;
+                    case RUNNING_QUEUE:
+                        invokeNextAgent();
+                        break;
 
-                case FINAL:
-                    if (!mFinished) finalizeBackup();
-                    else {
-                        Slog.e(TAG, "Duplicate finish");
-                    }
-                    mFinished = true;
-                    break;
+                    case FINAL:
+                        if (!mFinished) finalizeBackup();
+                        else {
+                            Slog.e(TAG, "Duplicate finish");
+                        }
+                        mFinished = true;
+                        break;
+                }
             }
         }
 
@@ -2796,6 +2936,12 @@
         void finalizeBackup() {
             addBackupTrace("finishing");
 
+            // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
+            // backup.
+            for (BackupRequest req : mQueue) {
+                dataChangedImpl(req.packageName);
+            }
+
             // Either backup was successful, in which case we of course do not need
             // this pass's journal any more; or it failed, in which case we just
             // re-enqueued all of these packages in the current active journal.
@@ -2850,7 +2996,9 @@
 
             clearBackupTrace();
 
-            if (mStatus == BackupTransport.TRANSPORT_OK &&
+            unregisterTask();
+
+            if (!mCancelAll && mStatus == BackupTransport.TRANSPORT_OK &&
                     mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) {
                 Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups);
                 CountDownLatch latch = new CountDownLatch(1);
@@ -2862,8 +3010,12 @@
                                 mObserver, mMonitor, mUserInitiated);
                 // Acquiring wakelock for PerformFullTransportBackupTask before its start.
                 mWakelock.acquire();
-                (new Thread(task, "full-transport-requested")).start();
+                (new Thread(mFullBackupTask, "full-transport-requested")).start();
+            } else if (mCancelAll) {
+                mFullBackupTask.unregisterTask();
+                sendBackupFinished(mObserver, BackupManager.ERROR_BACKUP_CANCELLED);
             } else {
+                mFullBackupTask.unregisterTask();
                 switch (mStatus) {
                     case BackupTransport.TRANSPORT_OK:
                         sendBackupFinished(mObserver, BackupManager.SUCCESS);
@@ -2905,8 +3057,8 @@
             mBackupData = null;
             mNewState = null;
 
-            final int token = generateToken();
             boolean callingAgent = false;
+            mEphemeralOpToken = generateToken();
             try {
                 // Look up the package info & signatures.  This is first so that if it
                 // throws an exception, there's no file setup yet that would need to
@@ -2945,10 +3097,11 @@
 
                 // Initiate the target's backup pass
                 addBackupTrace("setting timeout");
-                prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
+                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this,
+                        OP_TYPE_WAIT);
                 addBackupTrace("calling agent doBackup()");
 
-                agent.doBackup(mSavedState, mBackupData, mNewState, quota, token,
+                agent.doBackup(mSavedState, mBackupData, mNewState, quota, mEphemeralOpToken,
                         mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Error invoking for backup on " + packageName + ". " + e);
@@ -3060,195 +3213,225 @@
         }
 
         @Override
+        @GuardedBy("mCancelLock")
         public void operationComplete(long unusedResult) {
-            // The agent reported back to us!
-
-            if (mBackupData == null) {
-                // This callback was racing with our timeout, so we've cleaned up the
-                // agent state already and are on to the next thing.  We have nothing
-                // further to do here: agent state having been cleared means that we've
-                // initiated the appropriate next operation.
-                final String pkg = (mCurrentPackage != null)
-                        ? mCurrentPackage.packageName : "[none]";
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "Callback after agent teardown: " + pkg);
+            removeOperation(mEphemeralOpToken);
+            synchronized (mCancelLock) {
+                // The agent reported back to us!
+                if (mFinished) {
+                    Slog.d(TAG, "operationComplete received after task finished.");
+                    return;
                 }
-                addBackupTrace("late opComplete; curPkg = " + pkg);
-                return;
-            }
 
-            final String pkgName = mCurrentPackage.packageName;
-            final long filepos = mBackupDataName.length();
-            FileDescriptor fd = mBackupData.getFileDescriptor();
-            try {
-                // If it's a 3rd party app, see whether they wrote any protected keys
-                // and complain mightily if they are attempting shenanigans.
-                if (mCurrentPackage.applicationInfo != null &&
-                        (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
-                    ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
-                            ParcelFileDescriptor.MODE_READ_ONLY);
-                    BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
-                    try {
-                        while (in.readNextHeader()) {
-                            final String key = in.getKey();
-                            if (key != null && key.charAt(0) >= 0xff00) {
-                                // Not okay: crash them and bail.
-                                failAgent(mAgentBinder, "Illegal backup key: " + key);
-                                addBackupTrace("illegal key " + key + " from " + pkgName);
-                                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
-                                        "bad key");
-                                mBackupHandler.removeMessages(MSG_TIMEOUT);
-                                sendBackupOnPackageResult(mObserver, pkgName,
-                                        BackupManager.ERROR_AGENT_FAILURE);
-                                errorCleanup();
-                                // errorCleanup() implicitly executes next state properly
-                                return;
-                            }
-                            in.skipEntityData();
-                        }
-                    } finally {
-                        if (readFd != null) {
-                            readFd.close();
-                        }
+                if (mBackupData == null) {
+                    // This callback was racing with our timeout, so we've cleaned up the
+                    // agent state already and are on to the next thing.  We have nothing
+                    // further to do here: agent state having been cleared means that we've
+                    // initiated the appropriate next operation.
+                    final String pkg = (mCurrentPackage != null)
+                            ? mCurrentPackage.packageName : "[none]";
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Callback after agent teardown: " + pkg);
                     }
+                    addBackupTrace("late opComplete; curPkg = " + pkg);
+                    return;
                 }
 
-                // Piggyback the widget state payload, if any
-                writeWidgetPayloadIfAppropriate(fd, pkgName);
-            } catch (IOException e) {
-                // Hard disk error; recovery/failure policy TBD.  For now roll back,
-                // but we may want to consider this a transport-level failure (i.e.
-                // we're in such a bad state that we can't contemplate doing backup
-                // operations any more during this pass).
-                Slog.w(TAG, "Unable to save widget state for " + pkgName);
+                final String pkgName = mCurrentPackage.packageName;
+                final long filepos = mBackupDataName.length();
+                FileDescriptor fd = mBackupData.getFileDescriptor();
                 try {
-                    Os.ftruncate(fd, filepos);
-                } catch (ErrnoException ee) {
-                    Slog.w(TAG, "Unable to roll back!");
-                }
-            }
-
-            // Spin the data off to the transport and proceed with the next stage.
-            if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
-                    + pkgName);
-            mBackupHandler.removeMessages(MSG_TIMEOUT);
-            clearAgentState();
-            addBackupTrace("operation complete");
-
-            ParcelFileDescriptor backupData = null;
-            mStatus = BackupTransport.TRANSPORT_OK;
-            long size = 0;
-            try {
-                size = mBackupDataName.length();
-                if (size > 0) {
-                    if (mStatus == BackupTransport.TRANSPORT_OK) {
-                        backupData = ParcelFileDescriptor.open(mBackupDataName,
+                    // If it's a 3rd party app, see whether they wrote any protected keys
+                    // and complain mightily if they are attempting shenanigans.
+                    if (mCurrentPackage.applicationInfo != null &&
+                            (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+                                    == 0) {
+                        ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
                                 ParcelFileDescriptor.MODE_READ_ONLY);
-                        addBackupTrace("sending data to transport");
-                        int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
-                        mStatus = mTransport.performBackup(mCurrentPackage, backupData, flags);
+                        BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
+                        try {
+                            while (in.readNextHeader()) {
+                                final String key = in.getKey();
+                                if (key != null && key.charAt(0) >= 0xff00) {
+                                    // Not okay: crash them and bail.
+                                    failAgent(mAgentBinder, "Illegal backup key: " + key);
+                                    addBackupTrace("illegal key " + key + " from " + pkgName);
+                                    EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
+                                            "bad key");
+                                    mBackupHandler.removeMessages(MSG_TIMEOUT);
+                                    sendBackupOnPackageResult(mObserver, pkgName,
+                                            BackupManager.ERROR_AGENT_FAILURE);
+                                    errorCleanup();
+                                    // agentErrorCleanup() implicitly executes next state properly
+                                    return;
+                                }
+                                in.skipEntityData();
+                            }
+                        } finally {
+                            if (readFd != null) {
+                                readFd.close();
+                            }
+                        }
                     }
 
-                    // TODO - We call finishBackup() for each application backed up, because
-                    // we need to know now whether it succeeded or failed.  Instead, we should
-                    // hold off on finishBackup() until the end, which implies holding off on
-                    // renaming *all* the output state files (see below) until that happens.
-
-                    addBackupTrace("data delivered: " + mStatus);
-                    if (mStatus == BackupTransport.TRANSPORT_OK) {
-                        addBackupTrace("finishing op on transport");
-                        mStatus = mTransport.finishBackup();
-                        addBackupTrace("finished: " + mStatus);
-                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-                        addBackupTrace("transport rejected package");
+                    // Piggyback the widget state payload, if any
+                    writeWidgetPayloadIfAppropriate(fd, pkgName);
+                } catch (IOException e) {
+                    // Hard disk error; recovery/failure policy TBD.  For now roll back,
+                    // but we may want to consider this a transport-level failure (i.e.
+                    // we're in such a bad state that we can't contemplate doing backup
+                    // operations any more during this pass).
+                    Slog.w(TAG, "Unable to save widget state for " + pkgName);
+                    try {
+                        Os.ftruncate(fd, filepos);
+                    } catch (ErrnoException ee) {
+                        Slog.w(TAG, "Unable to roll back!");
                     }
-                } else {
-                    if (MORE_DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
-                    addBackupTrace("no data to send");
                 }
 
-                if (mStatus == BackupTransport.TRANSPORT_OK) {
-                    // After successful transport, delete the now-stale data
-                    // and juggle the files so that next time we supply the agent
-                    // with the new state file it just created.
-                    mBackupDataName.delete();
-                    mNewStateName.renameTo(mSavedStateName);
-                    sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS);
-                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
-                    logBackupComplete(pkgName);
-                } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-                    // The transport has rejected backup of this specific package.  Roll it
-                    // back but proceed with running the rest of the queue.
-                    mBackupDataName.delete();
-                    mNewStateName.delete();
-                    sendBackupOnPackageResult(mObserver, pkgName,
-                            BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
-                    EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
-                } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
-                    sendBackupOnPackageResult(mObserver, pkgName,
-                            BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
-                    EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
-                } else {
-                    // Actual transport-level failure to communicate the data to the backend
+                // Spin the data off to the transport and proceed with the next stage.
+                if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
+                        + pkgName);
+                mBackupHandler.removeMessages(MSG_TIMEOUT);
+                clearAgentState();
+                addBackupTrace("operation complete");
+
+                ParcelFileDescriptor backupData = null;
+                mStatus = BackupTransport.TRANSPORT_OK;
+                long size = 0;
+                try {
+                    size = mBackupDataName.length();
+                    if (size > 0) {
+                        if (mStatus == BackupTransport.TRANSPORT_OK) {
+                            backupData = ParcelFileDescriptor.open(mBackupDataName,
+                                    ParcelFileDescriptor.MODE_READ_ONLY);
+                            addBackupTrace("sending data to transport");
+                            int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+                            mStatus = mTransport.performBackup(mCurrentPackage, backupData, flags);
+                        }
+
+                        // TODO - We call finishBackup() for each application backed up, because
+                        // we need to know now whether it succeeded or failed.  Instead, we should
+                        // hold off on finishBackup() until the end, which implies holding off on
+                        // renaming *all* the output state files (see below) until that happens.
+
+                        addBackupTrace("data delivered: " + mStatus);
+                        if (mStatus == BackupTransport.TRANSPORT_OK) {
+                            addBackupTrace("finishing op on transport");
+                            mStatus = mTransport.finishBackup();
+                            addBackupTrace("finished: " + mStatus);
+                        } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+                            addBackupTrace("transport rejected package");
+                        }
+                    } else {
+                        if (MORE_DEBUG) Slog.i(TAG,
+                                "no backup data written; not calling transport");
+                        addBackupTrace("no data to send");
+                    }
+
+                    if (mStatus == BackupTransport.TRANSPORT_OK) {
+                        // After successful transport, delete the now-stale data
+                        // and juggle the files so that next time we supply the agent
+                        // with the new state file it just created.
+                        mBackupDataName.delete();
+                        mNewStateName.renameTo(mSavedStateName);
+                        sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS);
+                        EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
+                        logBackupComplete(pkgName);
+                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+                        // The transport has rejected backup of this specific package.  Roll it
+                        // back but proceed with running the rest of the queue.
+                        mBackupDataName.delete();
+                        mNewStateName.delete();
+                        sendBackupOnPackageResult(mObserver, pkgName,
+                                BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+                        EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
+                    } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+                        sendBackupOnPackageResult(mObserver, pkgName,
+                                BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
+                        EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
+                    } else {
+                        // Actual transport-level failure to communicate the data to the backend
+                        sendBackupOnPackageResult(mObserver, pkgName,
+                                BackupManager.ERROR_TRANSPORT_ABORTED);
+                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
+                    }
+                } catch (Exception e) {
                     sendBackupOnPackageResult(mObserver, pkgName,
                             BackupManager.ERROR_TRANSPORT_ABORTED);
+                    Slog.e(TAG, "Transport error backing up " + pkgName, e);
                     EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
-                }
-            } catch (Exception e) {
-                sendBackupOnPackageResult(mObserver, pkgName,
-                        BackupManager.ERROR_TRANSPORT_ABORTED);
-                Slog.e(TAG, "Transport error backing up " + pkgName, e);
-                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
-                mStatus = BackupTransport.TRANSPORT_ERROR;
-            } finally {
-                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
-            }
-
-            final BackupState nextState;
-            if (mStatus == BackupTransport.TRANSPORT_OK
-                    || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-                // Success or single-package rejection.  Proceed with the next app if any,
-                // otherwise we're done.
-                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
-            } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Package " + mCurrentPackage.packageName +
-                            " hit quota limit on k/v backup");
-                }
-                if (mAgentBinder != null) {
+                    mStatus = BackupTransport.TRANSPORT_ERROR;
+                } finally {
                     try {
-                        long quota = mTransport.getBackupQuota(mCurrentPackage.packageName, false);
-                        mAgentBinder.doQuotaExceeded(size, quota);
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
+                        if (backupData != null) backupData.close();
+                    } catch (IOException e) {
                     }
                 }
-                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
-            } else {
-                // Any other error here indicates a transport-level failure.  That means
-                // we need to halt everything and reschedule everything for next time.
-                revertAndEndBackup();
-                nextState = BackupState.FINAL;
-            }
 
-            executeNextState(nextState);
+                final BackupState nextState;
+                if (mStatus == BackupTransport.TRANSPORT_OK
+                        || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+                    // Success or single-package rejection.  Proceed with the next app if any,
+                    // otherwise we're done.
+                    nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+                } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+                    if (MORE_DEBUG) {
+                        Slog.d(TAG, "Package " + mCurrentPackage.packageName +
+                                " hit quota limit on k/v backup");
+                    }
+                    if (mAgentBinder != null) {
+                        try {
+                            long quota = mTransport.getBackupQuota(mCurrentPackage.packageName,
+                                    false);
+                            mAgentBinder.doQuotaExceeded(size, quota);
+                        } catch (Exception e) {
+                            Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
+                        }
+                    }
+                    nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+                } else {
+                    // Any other error here indicates a transport-level failure.  That means
+                    // we need to halt everything and reschedule everything for next time.
+                    revertAndEndBackup();
+                    nextState = BackupState.FINAL;
+                }
+
+                executeNextState(nextState);
+            }
         }
 
+
         @Override
-        public void handleTimeout() {
-            // Whoops, the current agent timed out running doBackup().  Tidy up and restage
-            // it for the next time we run a backup pass.
-            // !!! TODO: keep track of failure counts per agent, and blacklist those which
-            // fail repeatedly (i.e. have proved themselves to be buggy).
-            Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
-            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
-                    "timeout");
-            mMonitor = monitorEvent(mMonitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT,
-                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
-            addBackupTrace("timeout of " + mCurrentPackage.packageName);
-            errorCleanup();
-            dataChangedImpl(mCurrentPackage.packageName);
+        @GuardedBy("mCancelLock")
+        public void handleCancel(boolean cancelAll) {
+            removeOperation(mEphemeralOpToken);
+            synchronized (mCancelLock) {
+                if (mFinished) {
+                    // We have already cancelled this operation.
+                    if (MORE_DEBUG) {
+                        Slog.d(TAG, "Ignoring stale cancel. cancelAll=" + cancelAll);
+                    }
+                    return;
+                }
+                mCancelAll = cancelAll;
+                // Whoops, the current agent timed out running doBackup().  Tidy up and restage
+                // it for the next time we run a backup pass.
+                // !!! TODO: keep track of failure counts per agent, and blacklist those which
+                // fail repeatedly (i.e. have proved themselves to be buggy).
+                Slog.e(TAG, "Cancel backing up " + mCurrentPackage.packageName);
+                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName);
+                addBackupTrace(
+                        "cancel of " + mCurrentPackage.packageName + ", cancelAll=" + cancelAll);
+                errorCleanup();
+                if (!cancelAll) {
+                    executeNextState(
+                            mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
+                    dataChangedImpl(mCurrentPackage.packageName);
+                } else {
+                    finalizeBackup();
+                }
+            }
         }
 
         void revertAndEndBackup() {
@@ -3276,8 +3459,6 @@
             mBackupDataName.delete();
             mNewStateName.delete();
             clearAgentState();
-
-            executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
         }
 
         // Cleanup common to both success and failure cases
@@ -3289,7 +3470,7 @@
                 // Current-operation callback handling requires the validity of these various
                 // bits of internal state as an invariant of the operation still being live.
                 // This means we make sure to clear all of the state in unison inside the lock.
-                mCurrentOperations.clear();
+                mCurrentOperations.remove(mEphemeralOpToken);
                 mSavedState = mBackupData = mNewState = null;
             }
 
@@ -3343,7 +3524,7 @@
             try {
                 pipes = ParcelFileDescriptor.createPipe();
                 int token = generateToken();
-                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
+                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_WAIT);
                 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
                 routeSocketDataToOutput(pipes[0], out);
                 success = waitUntilOperationComplete(token);
@@ -3481,6 +3662,7 @@
         boolean mIncludeApks;
         PackageInfo mPkg;
         private final long mQuota;
+        private final int mOpToken;
 
         class FullBackupRunner implements Runnable {
             PackageInfo mPackage;
@@ -3535,7 +3717,7 @@
 
                     if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
                     prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL,
-                            mTimeoutMonitor /* in parent class */);
+                            mTimeoutMonitor /* in parent class */, OP_TYPE_WAIT);
                     mAgent.doFullBackup(mPipe, mQuota, mToken, mBackupManagerBinder);
                 } catch (IOException e) {
                     Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
@@ -3551,7 +3733,7 @@
         }
 
         FullBackupEngine(OutputStream output, FullBackupPreflight preflightHook, PackageInfo pkg,
-                         boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota) {
+                         boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota, int opToken) {
             mOutput = output;
             mPreflightHook = preflightHook;
             mPkg = pkg;
@@ -3561,6 +3743,7 @@
             mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
             mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
             mQuota = quota;
+            mOpToken = opToken;
         }
 
         public int preflightCheck() throws RemoteException {
@@ -3603,9 +3786,8 @@
                     byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(mPkg.packageName,
                             UserHandle.USER_SYSTEM);
 
-                    final int token = generateToken();
                     FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1],
-                            token, sendApk, !isSharedStorage, widgetBlob);
+                            mOpToken, sendApk, !isSharedStorage, widgetBlob);
                     pipes[1].close();   // the runner has dup'd it
                     pipes[1] = null;
                     Thread t = new Thread(runner, "app-data-runner");
@@ -3614,7 +3796,7 @@
                     // Now pull data from the app and stuff it into the output
                     routeSocketDataToOutput(pipes[0], mOutput);
 
-                    if (!waitUntilOperationComplete(token)) {
+                    if (!waitUntilOperationComplete(mOpToken)) {
                         Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
                     } else {
                         if (MORE_DEBUG) {
@@ -3865,12 +4047,14 @@
         PackageInfo mCurrentTarget;
         String mCurrentPassword;
         String mEncryptPassword;
+        private final int mCurrentOpToken;
 
         PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
                 boolean includeApks, boolean includeObbs, boolean includeShared,
                 boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
                 boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) {
             super(observer);
+            mCurrentOpToken = generateToken();
             mLatch = latch;
 
             mOutputFile = fd;
@@ -4156,8 +4340,7 @@
                     final boolean isSharedStorage =
                             pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
 
-                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks,
-                            this /* BackupRestoreTask */, Long.MAX_VALUE /* quota */);
+                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, this, Long.MAX_VALUE, mCurrentOpToken);
                     sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
 
                     // Don't need to check preflight result as there is no preflight hook.
@@ -4190,9 +4373,6 @@
                 } catch (IOException e) {
                     /* nothing we can do about this */
                 }
-                synchronized (mCurrentOpLock) {
-                    mCurrentOperations.clear();
-                }
                 synchronized (mLatch) {
                     mLatch.set(true);
                     mLatch.notifyAll();
@@ -4216,29 +4396,68 @@
         }
 
         @Override
-        public void handleTimeout() {
+        public void handleCancel(boolean cancelAll) {
             final PackageInfo target = mCurrentTarget;
             if (DEBUG) {
-                Slog.w(TAG, "adb backup timeout of " + target);
+                Slog.w(TAG, "adb backup cancel of " + target);
             }
             if (target != null) {
                 tearDownAgentAndKill(mCurrentTarget.applicationInfo);
             }
+            removeOperation(mCurrentOpToken);
         }
     }
 
-    // Full backup task extension used for transport-oriented operation
-    class PerformFullTransportBackupTask extends FullBackupTask {
+    /**
+     * Full backup task extension used for transport-oriented operation.
+     *
+     * Flow:
+     * For each requested package:
+     *     - Spin off a new SinglePackageBackupRunner (mBackupRunner) for the current package.
+     *     - Wait until preflight is complete. (mBackupRunner.getPreflightResultBlocking())
+     *     - If preflight data size is within limit, start reading data from agent pipe and writing
+     *       to transport pipe. While there is data to send, call transport.sendBackupData(int) to
+     *       tell the transport how many bytes to expect on its pipe.
+     *     - After sending all data, call transport.finishBackup() if things went well. And
+     *       transport.cancelFullBackup() otherwise.
+     *
+     * Interactions with mCurrentOperations:
+     *     - An entry for this object is added to mCurrentOperations for the entire lifetime of this
+     *       object. Used to cancel the operation.
+     *     - SinglePackageBackupRunner and SinglePackageBackupPreflight will put ephemeral entries
+     *       to get timeouts or operation complete callbacks.
+     *
+     * Handling cancels:
+     *     - The contract we provide is that the task won't interact with the transport after
+     *       handleCancel() is done executing.
+     *     - This task blocks at 3 points: 1. Preflight result check 2. Reading on agent side pipe
+     *       and 3. Get backup result from mBackupRunner.
+     *     - Bubbling up handleCancel to mBackupRunner handles all 3: 1. Calls handleCancel on the
+     *       preflight operation which counts down on the preflight latch. 2. Tears down the agent,
+     *       so read() returns -1. 3. Notifies mCurrentOpLock which unblocks
+     *       mBackupRunner.getBackupResultBlocking().
+     */
+    class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
         static final String TAG = "PFTBT";
+
+        private final Object mCancelLock = new Object();
+
         ArrayList<PackageInfo> mPackages;
         PackageInfo mCurrentPackage;
         boolean mUpdateSchedule;
         CountDownLatch mLatch;
-        AtomicBoolean mKeepRunning;     // signal from job scheduler
         FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
         IBackupObserver mBackupObserver;
         IBackupManagerMonitor mMonitor;
         boolean mUserInitiated;
+        private volatile IBackupTransport mTransport;
+        SinglePackageBackupRunner mBackupRunner;
+        private final int mBackupRunnerOpToken;
+
+        // This is true when a backup operation for some package is in progress.
+        private volatile boolean mIsDoingBackup;
+        private volatile boolean mCancelAll;
+        private final int mCurrentOpToken;
 
         PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
                 String[] whichPackages, boolean updateSchedule,
@@ -4247,12 +4466,15 @@
             super(observer);
             mUpdateSchedule = updateSchedule;
             mLatch = latch;
-            mKeepRunning = new AtomicBoolean(true);
             mJob = runningJob;
             mPackages = new ArrayList<PackageInfo>(whichPackages.length);
             mBackupObserver = backupObserver;
             mMonitor = monitor;
             mUserInitiated = userInitiated;
+            mCurrentOpToken = generateToken();
+            mBackupRunnerOpToken = generateToken();
+
+            registerTask();
 
             for (String pkg : whichPackages) {
                 try {
@@ -4298,12 +4520,60 @@
             }
         }
 
-        public void setRunning(boolean running) {
-            mKeepRunning.set(running);
+        private void registerTask() {
+            synchronized (mCurrentOpLock) {
+                Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
+                mCurrentOperations.put(mCurrentOpToken, new Operation(OP_PENDING, this,
+                        OP_TYPE_BACKUP));
+            }
+        }
+
+        private void unregisterTask() {
+            removeOperation(mCurrentOpToken);
+        }
+
+        @Override
+        public void execute() {
+            // Nothing to do.
+        }
+
+        @Override
+        public void handleCancel(boolean cancelAll) {
+            synchronized (mCancelLock) {
+                // We only support 'cancelAll = true' case for this task. Cancelling of a single package
+
+                // due to timeout is handled by SinglePackageBackupRunner and SinglePackageBackupPreflight.
+
+                if (!cancelAll) {
+                    Slog.wtf(TAG, "Expected cancelAll to be true.");
+                }
+
+                if (mCancelAll) {
+                    Slog.d(TAG, "Ignoring duplicate cancel call.");
+                    return;
+                }
+
+                mCancelAll = true;
+                if (mIsDoingBackup) {
+                    BackupManagerService.this.handleCancel(mBackupRunnerOpToken, cancelAll);
+                    try {
+                        mTransport.cancelFullBackup();
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Error calling cancelFullBackup() on transport: " + e);
+                        // Can't do much.
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void operationComplete(long result) {
+            // Nothing to do.
         }
 
         @Override
         public void run() {
+
             // data from the app, passed to us for bridging to the transport
             ParcelFileDescriptor[] enginePipes = null;
 
@@ -4325,8 +4595,8 @@
                     return;
                 }
 
-                IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
-                if (transport == null) {
+                mTransport = mTransportManager.getCurrentTransportBinder();
+                if (mTransport == null) {
                     Slog.w(TAG, "Transport not present; full data backup not performed");
                     backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
                     return;
@@ -4347,30 +4617,40 @@
 
                     // Tell the transport the data's coming
                     int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
-                    int backupPackageStatus = transport.performFullBackup(currentPackage,
-                            transportPipes[0], flags);
+                    int backupPackageStatus;
+                    long quota = Long.MAX_VALUE;
+                    synchronized (mCancelLock) {
+                        if (mCancelAll) {
+                            break;
+                        }
+                        backupPackageStatus = mTransport.performFullBackup(currentPackage,
+                                transportPipes[0], flags);
+
+                        if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
+                            quota = mTransport.getBackupQuota(currentPackage.packageName,
+                                    true /* isFullBackup */);
+                            // Now set up the backup engine / data source end of things
+                            enginePipes = ParcelFileDescriptor.createPipe();
+                            mBackupRunner =
+                                    new SinglePackageBackupRunner(enginePipes[1], currentPackage,
+                                            mTransport, quota, mBackupRunnerOpToken);
+                            // The runner dup'd the pipe half, so we close it here
+                            enginePipes[1].close();
+                            enginePipes[1] = null;
+
+                            mIsDoingBackup = true;
+                        }
+                    }
                     if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
-                        final long quota = transport.getBackupQuota(currentPackage.packageName,
-                                true /* isFullBackup */);
 
                         // The transport has its own copy of the read end of the pipe,
                         // so close ours now
                         transportPipes[0].close();
                         transportPipes[0] = null;
 
-                        // Now set up the backup engine / data source end of things
-                        enginePipes = ParcelFileDescriptor.createPipe();
-
-                        SinglePackageBackupRunner backupRunner =
-                                new SinglePackageBackupRunner(enginePipes[1], currentPackage,
-                                        transport, quota);
-                        // The runner dup'd the pipe half, so we close it here
-                        enginePipes[1].close();
-                        enginePipes[1] = null;
-
                         // Spin off the runner to fetch the app's data and pipe it
                         // into the engine pipes
-                        (new Thread(backupRunner, "package-backup-bridge")).start();
+                        (new Thread(mBackupRunner, "package-backup-bridge")).start();
 
                         // Read data off the engine pipe and pass it to the transport
                         // pipe until we hit EOD on the input stream.  We do not take
@@ -4380,7 +4660,7 @@
                         FileOutputStream out = new FileOutputStream(
                                 transportPipes[1].getFileDescriptor());
                         long totalRead = 0;
-                        final long preflightResult = backupRunner.getPreflightResultBlocking();
+                        final long preflightResult = mBackupRunner.getPreflightResultBlocking();
                         // Preflight result is negative if some error happened on preflight.
                         if (preflightResult < 0) {
                             if (MORE_DEBUG) {
@@ -4392,19 +4672,17 @@
                         } else {
                             int nRead = 0;
                             do {
-                                if (!mKeepRunning.get()) {
-                                    if (DEBUG_SCHEDULING) {
-                                        Slog.i(TAG, "Full backup task told to stop");
-                                    }
-                                    break;
-                                }
                                 nRead = in.read(buffer);
                                 if (MORE_DEBUG) {
                                     Slog.v(TAG, "in.read(buffer) from app: " + nRead);
                                 }
                                 if (nRead > 0) {
                                     out.write(buffer, 0, nRead);
-                                    backupPackageStatus = transport.sendBackupData(nRead);
+                                    synchronized (mCancelLock) {
+                                        if (!mCancelAll) {
+                                            backupPackageStatus = mTransport.sendBackupData(nRead);
+                                        }
+                                    }
                                     totalRead += nRead;
                                     if (mBackupObserver != null && preflightResult > 0) {
                                         sendBackupOnUpdate(mBackupObserver, packageName,
@@ -4413,29 +4691,32 @@
                                 }
                             } while (nRead > 0
                                     && backupPackageStatus == BackupTransport.TRANSPORT_OK);
-
                             // Despite preflight succeeded, package still can hit quota on flight.
                             if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
                                 Slog.w(TAG, "Package hit quota limit in-flight " + packageName
                                         + ": " + totalRead + " of " + quota);
-                                backupRunner.sendQuotaExceeded(totalRead, quota);
+                                mBackupRunner.sendQuotaExceeded(totalRead, quota);
                             }
                         }
 
-                        // If we've lost our running criteria, tell the transport to cancel
-                        // and roll back this (partial) backup payload; otherwise tell it
-                        // that we've reached the clean finish state.
-                        if (!mKeepRunning.get()) {
-                            backupPackageStatus = BackupTransport.TRANSPORT_ERROR;
-                            transport.cancelFullBackup();
-                        } else {
-                            // If we were otherwise in a good state, now interpret the final
-                            // result based on what finishBackup() returns.  If we're in a
-                            // failure case already, preserve that result and ignore whatever
-                            // finishBackup() reports.
-                            final int finishResult = transport.finishBackup();
-                            if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
-                                backupPackageStatus = finishResult;
+                        final int backupRunnerResult = mBackupRunner.getBackupResultBlocking();
+
+                        synchronized (mCancelLock) {
+                            mIsDoingBackup = false;
+                            // If mCancelCurrent is true, we have already called cancelFullBackup().
+                            if (!mCancelAll) {
+                                if (backupRunnerResult == BackupTransport.TRANSPORT_OK) {
+                                    // If we were otherwise in a good state, now interpret the final
+                                    // result based on what finishBackup() returns.  If we're in a
+                                    // failure case already, preserve that result and ignore whatever
+                                    // finishBackup() reports.
+                                    final int finishResult = mTransport.finishBackup();
+                                    if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
+                                        backupPackageStatus = finishResult;
+                                    }
+                                } else {
+                                    mTransport.cancelFullBackup();
+                                }
                             }
                         }
 
@@ -4449,8 +4730,7 @@
                         // errors take precedence over agent/app-specific errors for purposes of
                         // determining our course of action.
                         if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
-                            // We still could fail in backup runner thread, getting result from there.
-                            int backupRunnerResult = backupRunner.getBackupResultBlocking();
+                            // We still could fail in backup runner thread.
                             if (backupRunnerResult != BackupTransport.TRANSPORT_OK) {
                                 // If there was an error in runner thread and
                                 // not TRANSPORT_ERROR here, overwrite it.
@@ -4474,7 +4754,7 @@
 
                         // Also ask the transport how long it wants us to wait before
                         // moving on to the next package, if any.
-                        backoff = transport.requestFullBackupTime();
+                        backoff = mTransport.requestFullBackupTime();
                         if (DEBUG_SCHEDULING) {
                             Slog.i(TAG, "Transport suggested backoff=" + backoff);
                         }
@@ -4513,6 +4793,14 @@
                         EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
                         tearDownAgentAndKill(currentPackage.applicationInfo);
                         // Do nothing, clean up, and continue looping.
+                    } else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) {
+                        sendBackupOnPackageResult(mBackupObserver, packageName,
+                                BackupManager.ERROR_BACKUP_CANCELLED);
+                        Slog.w(TAG, "Backup cancelled. package=" + packageName +
+                                ", cancelAll=" + mCancelAll);
+                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
+                        tearDownAgentAndKill(currentPackage.applicationInfo);
+                        // Do nothing, clean up, and continue looping.
                     } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
                         sendBackupOnPackageResult(mBackupObserver, packageName,
                             BackupManager.ERROR_TRANSPORT_ABORTED);
@@ -4542,6 +4830,11 @@
                 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
                 Slog.w(TAG, "Exception trying full transport backup", e);
             } finally {
+
+                if (mCancelAll) {
+                    backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED;
+                }
+
                 if (DEBUG) {
                     Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
                 }
@@ -4550,6 +4843,8 @@
                 cleanUpPipes(transportPipes);
                 cleanUpPipes(enginePipes);
 
+                unregisterTask();
+
                 if (mJob != null) {
                     mJob.finishBackupPass();
                 }
@@ -4565,6 +4860,7 @@
                 if (mUpdateSchedule) {
                     scheduleNextFullBackupJob(backoff);
                 }
+
                 Slog.i(BackupManagerService.TAG, "Full data backup pass finished.");
                 mWakelock.release();
             }
@@ -4601,23 +4897,24 @@
             final CountDownLatch mLatch = new CountDownLatch(1);
             final IBackupTransport mTransport;
             final long mQuota;
+            private final int mCurrentOpToken;
 
-            public SinglePackageBackupPreflight(IBackupTransport transport, long quota) {
+            SinglePackageBackupPreflight(IBackupTransport transport, long quota, int currentOpToken) {
                 mTransport = transport;
                 mQuota = quota;
+                mCurrentOpToken = currentOpToken;
             }
 
             @Override
             public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
                 int result;
                 try {
-                    final int token = generateToken();
-                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
+                    prepareOperationTimeout(mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_WAIT);
                     addBackupTrace("preflighting");
                     if (MORE_DEBUG) {
                         Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
                     }
-                    agent.doMeasureFullBackup(mQuota, token, mBackupManagerBinder);
+                    agent.doMeasureFullBackup(mQuota, mCurrentOpToken, mBackupManagerBinder);
 
                     // Now wait to get our result back.  If this backstop timeout is reached without
                     // the latch being thrown, flow will continue as though a result or "normal"
@@ -4652,7 +4949,7 @@
 
             @Override
             public void execute() {
-                // Unused in this case
+                // Unused.
             }
 
             @Override
@@ -4663,15 +4960,17 @@
                 }
                 mResult.set(result);
                 mLatch.countDown();
+                removeOperation(mCurrentOpToken);
             }
 
             @Override
-            public void handleTimeout() {
+            public void handleCancel(boolean cancelAll) {
                 if (MORE_DEBUG) {
-                    Slog.i(TAG, "Preflight timeout; failing");
+                    Slog.i(TAG, "Preflight cancelled; failing");
                 }
                 mResult.set(BackupTransport.AGENT_ERROR);
                 mLatch.countDown();
+                removeOperation(mCurrentOpToken);
             }
 
             @Override
@@ -4688,43 +4987,67 @@
         class SinglePackageBackupRunner implements Runnable, BackupRestoreTask {
             final ParcelFileDescriptor mOutput;
             final PackageInfo mTarget;
-            final FullBackupPreflight mPreflight;
+            final SinglePackageBackupPreflight mPreflight;
             final CountDownLatch mPreflightLatch;
             final CountDownLatch mBackupLatch;
+            private final int mCurrentOpToken;
+            private final int mEphemeralToken;
             private FullBackupEngine mEngine;
             private volatile int mPreflightResult;
             private volatile int mBackupResult;
             private final long mQuota;
+            private volatile boolean mIsCancelled;
 
             SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
-                    IBackupTransport transport, long quota) throws IOException {
+                    IBackupTransport transport, long quota, int currentOpToken) throws IOException {
                 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
                 mTarget = target;
-                mPreflight = new SinglePackageBackupPreflight(transport, quota);
+                mCurrentOpToken = currentOpToken;
+                mEphemeralToken = generateToken();
+                mPreflight = new SinglePackageBackupPreflight(transport, quota, mEphemeralToken);
                 mPreflightLatch = new CountDownLatch(1);
                 mBackupLatch = new CountDownLatch(1);
                 mPreflightResult = BackupTransport.AGENT_ERROR;
                 mBackupResult = BackupTransport.AGENT_ERROR;
                 mQuota = quota;
+                registerTask();
+            }
+
+            void registerTask() {
+                synchronized (mCurrentOpLock) {
+                    mCurrentOperations.put(mCurrentOpToken, new Operation(OP_PENDING, this,
+                            OP_TYPE_WAIT));
+                }
+            }
+
+            void unregisterTask() {
+                synchronized (mCurrentOpLock) {
+                    mCurrentOperations.remove(mCurrentOpToken);
+                }
             }
 
             @Override
             public void run() {
                 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
-                mEngine = new FullBackupEngine(out, mPreflight, mTarget, false, this, mQuota);
+                mEngine = new FullBackupEngine(out, mPreflight, mTarget, false, this, mQuota, mCurrentOpToken);
                 try {
                     try {
-                        mPreflightResult = mEngine.preflightCheck();
+                        if (!mIsCancelled) {
+                            mPreflightResult = mEngine.preflightCheck();
+                        }
                     } finally {
                         mPreflightLatch.countDown();
                     }
                     // If there is no error on preflight, continue backup.
                     if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
-                        mBackupResult = mEngine.backupOnePackage();
+                        if (!mIsCancelled) {
+                            mBackupResult = mEngine.backupOnePackage();
+                        }
                     }
                 } catch (Exception e) {
                     Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName);
                 } finally {
+                    unregisterTask();
                     mBackupLatch.countDown();
                     try {
                         mOutput.close();
@@ -4743,6 +5066,9 @@
             long getPreflightResultBlocking() {
                 try {
                     mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                    if (mIsCancelled) {
+                        return BackupManager.ERROR_BACKUP_CANCELLED;
+                    }
                     if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
                         return mPreflight.getExpectedSizeOrErrorCode();
                     } else {
@@ -4756,6 +5082,9 @@
             int getBackupResultBlocking() {
                 try {
                     mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                    if (mIsCancelled) {
+                        return BackupManager.ERROR_BACKUP_CANCELLED;
+                    }
                     return mBackupResult;
                 } catch (InterruptedException e) {
                     return BackupTransport.AGENT_ERROR;
@@ -4772,14 +5101,23 @@
             public void operationComplete(long result) { /* intentionally empty */ }
 
             @Override
-            public void handleTimeout() {
+            public void handleCancel(boolean cancelAll) {
                 if (DEBUG) {
-                    Slog.w(TAG, "Full backup timeout of " + mTarget.packageName);
+                    Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
                 }
+
                 mMonitor = monitorEvent(mMonitor,
                         BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_TIMEOUT,
                         mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
+                mIsCancelled = true;
+                // Cancel tasks spun off by this task.
+                BackupManagerService.this.handleCancel(mEphemeralToken, cancelAll);
                 tearDownAgentAndKill(mTarget.applicationInfo);
+                // Free up everyone waiting on this task and its children.
+                mPreflightLatch.countDown();
+                mBackupLatch.countDown();
+                // We are done with this operation.
+                removeOperation(mCurrentOpToken);
             }
         }
     }
@@ -5057,7 +5395,7 @@
                 if (DEBUG_SCHEDULING) {
                     Slog.i(TAG, "Telling running backup to stop");
                 }
-                mRunningFullBackupTask.setRunning(false);
+                mRunningFullBackupTask.handleCancel(true);
             }
         }
     }
@@ -5191,6 +5529,8 @@
         // Widget blob to be restored out-of-band
         byte[] mWidgetData = null;
 
+        private final int mEphemeralOpToken;
+
         // Runner that can be placed in a separate thread to do in-process
         // invocations of the full restore API asynchronously. Used by adb restore.
         class RestoreFileRunnable implements Runnable {
@@ -5226,7 +5566,9 @@
         }
 
         public FullRestoreEngine(BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
-                PackageInfo onlyPackage, boolean allowApks, boolean allowObbs) {
+                PackageInfo onlyPackage, boolean allowApks, boolean allowObbs,
+                int ephemeralOpToken) {
+            mEphemeralOpToken = ephemeralOpToken;
             mMonitorTask = monitorTask;
             mObserver = observer;
             mOnlyPackage = onlyPackage;
@@ -5431,17 +5773,16 @@
                         if (okay) {
                             boolean agentSuccess = true;
                             long toCopy = info.size;
-                            final int token = generateToken();
                             try {
-                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL,
-                                        mMonitorTask);
+                                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_FULL_BACKUP_INTERVAL,
+                                        mMonitorTask, OP_TYPE_WAIT);
 
                                 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
                                     if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
                                             + " : " + info.path);
                                     mObbConnection.restoreObbFile(pkg, mPipes[0],
                                             info.size, info.type, info.path, info.mode,
-                                            info.mtime, token, mBackupManagerBinder);
+                                            info.mtime, mEphemeralOpToken, mBackupManagerBinder);
                                 } else {
                                     if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file "
                                             + info.path);
@@ -5452,12 +5793,12 @@
                                     if (mTargetApp.processName.equals("system")) {
                                         Slog.d(TAG, "system process agent - spinning a thread");
                                         RestoreFileRunnable runner = new RestoreFileRunnable(
-                                                mAgent, info, mPipes[0], token);
+                                                mAgent, info, mPipes[0], mEphemeralOpToken);
                                         new Thread(runner, "restore-sys-runner").start();
                                     } else {
                                         mAgent.doRestoreFile(mPipes[0], info.size, info.type,
                                                 info.domain, info.path, info.mode, info.mtime,
-                                                token, mBackupManagerBinder);
+                                                mEphemeralOpToken, mBackupManagerBinder);
                                     }
                                 }
                             } catch (IOException e) {
@@ -5509,7 +5850,7 @@
 
                                 // and now that we've sent it all, wait for the remote
                                 // side to acknowledge receipt
-                                agentSuccess = waitUntilOperationComplete(token);
+                                agentSuccess = waitUntilOperationComplete(mEphemeralOpToken);
                             }
 
                             // okay, if the remote end failed at any point, deal with
@@ -6354,9 +6695,11 @@
     class AdbRestoreFinishedLatch implements BackupRestoreTask {
         static final String TAG = "AdbRestoreFinishedLatch";
         final CountDownLatch mLatch;
+        private final int mCurrentOpToken;
 
-        AdbRestoreFinishedLatch() {
+        AdbRestoreFinishedLatch(int currentOpToken) {
             mLatch = new CountDownLatch(1);
+            mCurrentOpToken = currentOpToken;
         }
 
         void await() {
@@ -6379,14 +6722,16 @@
                 Slog.w(TAG, "adb onRestoreFinished() complete");
             }
             mLatch.countDown();
+            removeOperation(mCurrentOpToken);
         }
 
         @Override
-        public void handleTimeout() {
+        public void handleCancel(boolean cancelAll) {
             if (DEBUG) {
                 Slog.w(TAG, "adb onRestoreFinished() timed out");
             }
             mLatch.countDown();
+            removeOperation(mCurrentOpToken);
         }
     }
 
@@ -6577,9 +6922,6 @@
                     Slog.w(TAG, "Close of restore data pipe threw", e);
                     /* nothing we can do about this */
                 }
-                synchronized (mCurrentOpLock) {
-                    mCurrentOperations.clear();
-                }
                 synchronized (mLatchObject) {
                     mLatchObject.set(true);
                     mLatchObject.notifyAll();
@@ -6868,7 +7210,8 @@
                             long toCopy = info.size;
                             final int token = generateToken();
                             try {
-                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
+                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null,
+                                        OP_TYPE_WAIT);
                                 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
                                     if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
                                             + " : " + info.path);
@@ -7003,8 +7346,9 @@
                     // In the adb restore case, we do restore-finished here
                     if (doRestoreFinished) {
                         final int token = generateToken();
-                        final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch();
-                        prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch);
+                        final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(token);
+                        prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch,
+                                OP_TYPE_WAIT);
                         if (mTargetApp.processName.equals("system")) {
                             if (MORE_DEBUG) {
                                 Slog.d(TAG, "system agent - restoreFinished on thread");
@@ -7872,11 +8216,14 @@
         ParcelFileDescriptor mBackupData;
         ParcelFileDescriptor mNewState;
 
+        private final int mEphemeralOpToken;
+
         // Invariant: mWakelock is already held, and this task is responsible for
         // releasing it at the end of the restore operation.
         PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
                 IBackupManagerMonitor monitor, long restoreSetToken, PackageInfo targetPackage,
                 int pmToken, boolean isFullSystemRestore, String[] filterSet) {
+            mEphemeralOpToken = generateToken();
             mState = UnifiedRestoreState.INITIAL;
             mStartRealtime = SystemClock.elapsedRealtime();
 
@@ -8304,7 +8651,6 @@
             ParcelFileDescriptor stage;
             File downloadFile = (staging) ? mStageName : mBackupDataName;
 
-            final int token = generateToken();
             try {
                 // Run the transport's restore pass
                 stage = ParcelFileDescriptor.open(downloadFile,
@@ -8377,9 +8723,9 @@
                 // Kick off the restore, checking for hung agents.  The timeout or
                 // the operationComplete() callback will schedule the next step,
                 // so we do not do that here.
-                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
+                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_WAIT);
                 mAgent.doRestore(mBackupData, appVersionCode, mNewState,
-                        token, mBackupManagerBinder);
+                        mEphemeralOpToken, mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
@@ -8426,9 +8772,9 @@
         // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
         private void restoreFinished() {
             try {
-                final int token = generateToken();
-                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
-                mAgent.doRestoreFinished(token, mBackupManagerBinder);
+                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
+                        OP_TYPE_WAIT);
+                mAgent.doRestoreFinished(mEphemeralOpToken, mBackupManagerBinder);
                 // If we get this far, the callback or timeout will schedule the
                 // next restore state, so we're done
             } catch (Exception e) {
@@ -8452,7 +8798,10 @@
             // pipe through which the engine will read data.  [0] read, [1] write
             ParcelFileDescriptor[] mEnginePipes;
 
+            private final int mEphemeralOpToken;
+
             public StreamFeederThread() throws IOException {
+                mEphemeralOpToken = generateToken();
                 mTransportPipes = ParcelFileDescriptor.createPipe();
                 mEnginePipes = ParcelFileDescriptor.createPipe();
                 setRunning(true);
@@ -8466,7 +8815,7 @@
                 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
                         mCurrentPackage.packageName);
 
-                mEngine = new FullRestoreEngine(this, null, mCurrentPackage, false, false);
+                mEngine = new FullRestoreEngine(this, null, mCurrentPackage, false, false, mEphemeralOpToken);
                 mEngineThread = new EngineThread(mEngine, mEnginePipes[0]);
 
                 ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
@@ -8605,7 +8954,8 @@
 
             // The app has timed out handling a restoring file
             @Override
-            public void handleTimeout() {
+            public void handleCancel(boolean cancelAll) {
+                removeOperation(mEphemeralOpToken);
                 if (DEBUG) {
                     Slog.w(TAG, "Full-data restore target timed out; shutting down");
                 }
@@ -8785,13 +9135,11 @@
             // The caller is responsible for reestablishing the state machine; our
             // responsibility here is to clear the decks for whatever comes next.
             mBackupHandler.removeMessages(MSG_TIMEOUT, this);
-            synchronized (mCurrentOpLock) {
-                mCurrentOperations.clear();
-            }
         }
 
         @Override
         public void operationComplete(long unusedResult) {
+            removeOperation(mEphemeralOpToken);
             if (MORE_DEBUG) {
                 Slog.i(TAG, "operationComplete() during restore: target="
                         + mCurrentPackage.packageName
@@ -8852,7 +9200,8 @@
 
         // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
         @Override
-        public void handleTimeout() {
+        public void handleCancel(boolean cancelAll) {
+            removeOperation(mEphemeralOpToken);
             Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
             mMonitor = monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
                     mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
@@ -9966,7 +10315,16 @@
                     // and ignore it; we've already dealt with the timeout.
                     op = null;
                     mCurrentOperations.delete(token);
-                } else {
+                } else if (op.state == OP_ACKNOWLEDGED) {
+                    if (DEBUG) {
+                        Slog.w(TAG, "Received duplicate ack for token=" +
+                                Integer.toHexString(token));
+                    }
+                    op = null;
+                    mCurrentOperations.remove(token);
+                } else if (op.state == OP_PENDING) {
+                    // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
+                    // called after we we receive this call.
                     op.state = OP_ACKNOWLEDGED;
                 }
             }
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index da0cee5..8855661 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -363,6 +363,14 @@
     }
 
     @Override
+    public void cancelBackups() throws RemoteException {
+        BackupManagerService svc = mService;
+        if (svc != null) {
+            svc.cancelBackups();
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
 
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 4559254..2e61550 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -132,6 +132,7 @@
 2843 full_backup_success (Package|3)
 2844 full_restore_package (Package|3)
 2845 full_backup_quota_exceeded (Package|3)
+2846 full_backup_cancelled (Package|3),(Message|3)
 
 2850 backup_transport_lifecycle (Transport|3),(Bound|1|1)
 
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 5935600..dc987fa 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -149,11 +149,15 @@
 import java.lang.annotation.Retention;
 import java.nio.charset.StandardCharsets;
 import java.security.InvalidParameterException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * This class provides a system service that manages input methods.
@@ -525,18 +529,188 @@
      * </p>
      */
     private static class StartInputInfo {
+        private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
+
+        final int mSequenceNumber;
+        final long mTimestamp;
+        final long mWallTime;
         @NonNull
         final IBinder mImeToken;
+        @NonNull
+        final String mImeId;
+        // @InputMethodClient.StartInputReason
+        final int mStartInputReason;
+        final boolean mRestarting;
         @Nullable
         final IBinder mTargetWindow;
+        @NonNull
+        final EditorInfo mEditorInfo;
+        final int mTargetWindowSoftInputMode;
+        final int mClientBindSequenceNumber;
 
-        StartInputInfo(@NonNull IBinder imeToken, @Nullable IBinder targetWindow) {
+        StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId,
+                /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting,
+                @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo,
+                int targetWindowSoftInputMode, int clientBindSequenceNumber) {
+            mSequenceNumber = sSequenceNumber.getAndIncrement();
+            mTimestamp = SystemClock.uptimeMillis();
+            mWallTime = System.currentTimeMillis();
             mImeToken = imeToken;
+            mImeId = imeId;
+            mStartInputReason = startInputReason;
+            mRestarting = restarting;
             mTargetWindow = targetWindow;
+            mEditorInfo = editorInfo;
+            mTargetWindowSoftInputMode = targetWindowSoftInputMode;
+            mClientBindSequenceNumber = clientBindSequenceNumber;
         }
     }
 
-    private WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
+    @GuardedBy("mMethodMap")
+    private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
+
+    /**
+     * A ring buffer to store the history of {@link StartInputInfo}.
+     */
+    private static final class StartInputHistory {
+        /**
+         * Entry size for non low-RAM devices.
+         *
+         * <p>TODO: Consider to follow what other system services have been doing to manage
+         * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
+         */
+        private final static int ENTRY_SIZE_FOR_HIGH_RAM_DEVICE = 16;
+
+        /**
+         * Entry size for non low-RAM devices.
+         *
+         * <p>TODO: Consider to follow what other system services have been doing to manage
+         * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
+         */
+        private final static int ENTRY_SIZE_FOR_LOW_RAM_DEVICE = 5;
+
+        private static int getEntrySize() {
+            if (ActivityManager.isLowRamDeviceStatic()) {
+                return ENTRY_SIZE_FOR_LOW_RAM_DEVICE;
+            } else {
+                return ENTRY_SIZE_FOR_HIGH_RAM_DEVICE;
+            }
+        }
+
+        /**
+         * Backing store for the ring bugger.
+         */
+        private final Entry[] mEntries = new Entry[getEntrySize()];
+
+        /**
+         * An index of {@link #mEntries}, to which next {@link #addEntry(StartInputInfo)} should
+         * write.
+         */
+        private int mNextIndex = 0;
+
+        /**
+         * Recyclable entry to store the information in {@link StartInputInfo}.
+         */
+        private static final class Entry {
+            int mSequenceNumber;
+            long mTimestamp;
+            long mWallTime;
+            @NonNull
+            String mImeTokenString;
+            @NonNull
+            String mImeId;
+            /* @InputMethodClient.StartInputReason */
+            int mStartInputReason;
+            boolean mRestarting;
+            @NonNull
+            String mTargetWindowString;
+            @NonNull
+            EditorInfo mEditorInfo;
+            int mTargetWindowSoftInputMode;
+            int mClientBindSequenceNumber;
+
+            Entry(@NonNull StartInputInfo original) {
+                set(original);
+            }
+
+            void set(@NonNull StartInputInfo original) {
+                mSequenceNumber = original.mSequenceNumber;
+                mTimestamp = original.mTimestamp;
+                mWallTime = original.mWallTime;
+                // Intentionally convert to String so as not to keep a strong reference to a Binder
+                // object.
+                mImeTokenString = String.valueOf(original.mImeToken);
+                mImeId = original.mImeId;
+                mStartInputReason = original.mStartInputReason;
+                mRestarting = original.mRestarting;
+                // Intentionally convert to String so as not to keep a strong reference to a Binder
+                // object.
+                mTargetWindowString = String.valueOf(original.mTargetWindow);
+                mEditorInfo = original.mEditorInfo;
+                mTargetWindowSoftInputMode = original.mTargetWindowSoftInputMode;
+                mClientBindSequenceNumber = original.mClientBindSequenceNumber;
+            }
+        }
+
+        /**
+         * Add a new entry and discard the oldest entry as needed.
+         * @param info {@lin StartInputInfo} to be added.
+         */
+        void addEntry(@NonNull StartInputInfo info) {
+            final int index = mNextIndex;
+            if (mEntries[index] == null) {
+                mEntries[index] = new Entry(info);
+            } else {
+                mEntries[index].set(info);
+            }
+            mNextIndex = (mNextIndex + 1) % mEntries.length;
+        }
+
+        void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+            final SimpleDateFormat dataFormat =
+                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+
+            for (int i = 0; i < mEntries.length; ++i) {
+                final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
+                if (entry == null) {
+                    continue;
+                }
+                pw.print(prefix);
+                pw.println("StartInput #" + entry.mSequenceNumber + ":");
+
+                pw.print(prefix);
+                pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
+                        + " (timestamp=" + entry.mTimestamp + ")"
+                        + " reason="
+                        + InputMethodClient.getStartInputReason(entry.mStartInputReason)
+                        + " restarting=" + entry.mRestarting);
+
+                pw.print(prefix);
+                pw.println(" imeToken=" + entry.mImeTokenString + " [" + entry.mImeId + "]");
+
+                pw.print(prefix);
+                pw.println(" targetWin=" + entry.mTargetWindowString
+                        + " [" + entry.mEditorInfo.packageName + "]"
+                        + " clientBindSeq=" + entry.mClientBindSequenceNumber);
+
+                pw.print(prefix);
+                pw.println(" softInputMode=" + InputMethodClient.softInputModeToString(
+                                entry.mTargetWindowSoftInputMode));
+
+                pw.print(prefix);
+                pw.println(" inputType=0x" + Integer.toHexString(entry.mEditorInfo.inputType)
+                        + " imeOptions=0x" + Integer.toHexString(entry.mEditorInfo.imeOptions)
+                        + " fieldId=0x" + Integer.toHexString(entry.mEditorInfo.fieldId)
+                        + " fieldName=" + entry.mEditorInfo.fieldName
+                        + " actionId=" + entry.mEditorInfo.actionId
+                        + " actionLabel=" + entry.mEditorInfo.actionLabel);
+            }
+        }
+    }
+
+    @GuardedBy("mMethodMap")
+    @NonNull
+    private final StartInputHistory mStartInputHistory = new StartInputHistory();
 
     class SettingsObserver extends ContentObserver {
         int mUserId;
@@ -1379,8 +1553,11 @@
         }
 
         final Binder startInputToken = new Binder();
-        final StartInputInfo info = new StartInputInfo(mCurToken, mCurFocusedWindow);
+        final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
+                !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
+                mCurSeq);
         mStartInputMap.put(startInputToken, info);
+        mStartInputHistory.addEntry(info);
 
         final SessionState session = mCurClient.curSession;
         executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
@@ -1863,8 +2040,9 @@
             return;
         }
 
+        final StartInputInfo info;
         synchronized (mMethodMap) {
-            final StartInputInfo info = mStartInputMap.get(startInputToken);
+            info = mStartInputMap.get(startInputToken);
             if (info == null) {
                 throw new InvalidParameterException("Unknown startInputToken=" + startInputToken);
             }
@@ -1872,6 +2050,8 @@
             mBackDisposition = backDisposition;
             updateSystemUiLocked(token, vis, backDisposition);
         }
+        mWindowManagerInternal.updateInputMethodWindowStatus(info.mImeToken,
+                (vis & InputMethodService.IME_VISIBLE) != 0, info.mTargetWindow);
     }
 
     private void updateSystemUi(IBinder token, int vis, int backDisposition) {
@@ -4152,6 +4332,9 @@
             mSwitchingController.dump(p);
             p.println("  mSettings:");
             mSettings.dumpLocked(p, "    ");
+
+            p.println("  mStartInputHistory:");
+            mStartInputHistory.dump(pw, "   ");
         }
 
         p.println(" ");
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 0834eb8..ea33906 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -16,7 +16,6 @@
 
 package com.android.server;
 
-import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
@@ -142,6 +141,10 @@
 
     private ServiceState[] mServiceState;
 
+    private int[] mVoiceActivationState;
+
+    private int[] mDataActivationState;
+
     private SignalStrength[] mSignalStrength;
 
     private boolean[] mMessageWaiting;
@@ -301,6 +304,8 @@
         mDataConnectionNetworkType = new int[numPhones];
         mCallIncomingNumber = new String[numPhones];
         mServiceState = new ServiceState[numPhones];
+        mVoiceActivationState = new int[numPhones];
+        mDataActivationState = new int[numPhones];
         mSignalStrength = new SignalStrength[numPhones];
         mMessageWaiting = new boolean[numPhones];
         mDataConnectionPossible = new boolean[numPhones];
@@ -315,6 +320,8 @@
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
             mDataConnectionState[i] = TelephonyManager.DATA_UNKNOWN;
+            mVoiceActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
+            mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
             mCallIncomingNumber[i] =  "";
             mServiceState[i] =  new ServiceState();
             mSignalStrength[i] =  new SignalStrength();
@@ -644,6 +651,20 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) {
+                        try {
+                            r.callback.onVoiceActivationStateChanged(mVoiceActivationState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
+                    if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) {
+                        try {
+                            r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -795,6 +816,67 @@
         broadcastServiceStateChanged(state, phoneId, subId);
     }
 
+    public void notifySimActivationStateChangedForPhoneId(int phoneId, int subId,
+            int activationType, int activationState) {
+        if (!checkNotifyPermission("notifySimActivationState()")){
+            return;
+        }
+        if (VDBG) {
+            log("notifySimActivationStateForPhoneId: subId=" + subId + " phoneId=" + phoneId
+                    + "type=" + activationType + " state=" + activationState);
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                switch (activationType) {
+                    case PhoneConstants.SIM_ACTIVATION_TYPE_VOICE:
+                        mVoiceActivationState[phoneId] = activationState;
+                        break;
+                    case PhoneConstants.SIM_ACTIVATION_TYPE_DATA:
+                        mDataActivationState[phoneId] = activationState;
+                        break;
+                    default:
+                        return;
+                }
+                for (Record r : mRecords) {
+                    if (VDBG) {
+                        log("notifySimActivationStateForPhoneId: r=" + r + " subId=" + subId
+                                + " phoneId=" + phoneId + "type=" + activationType
+                                + " state=" + activationState);
+                    }
+                    try {
+                        if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_VOICE) &&
+                                r.matchPhoneStateListenerEvent(
+                                        PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) &&
+                                idMatch(r.subId, subId, phoneId)) {
+                            if (DBG) {
+                                log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
+                                        + " subId=" + subId + " phoneId=" + phoneId
+                                        + " state=" + activationState);
+                            }
+                            r.callback.onVoiceActivationStateChanged(activationState);
+                        }
+                        if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_DATA) &&
+                                r.matchPhoneStateListenerEvent(
+                                        PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) &&
+                                idMatch(r.subId, subId, phoneId)) {
+                            if (DBG) {
+                                log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
+                                        + " subId=" + subId + " phoneId=" + phoneId
+                                        + " state=" + activationState);
+                            }
+                            r.callback.onDataActivationStateChanged(activationState);
+                        }
+                    }  catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            } else {
+                log("notifySimActivationStateForPhoneId: INVALID phoneId=" + phoneId);
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     public void notifySignalStrengthForPhoneId(int phoneId, int subId,
                 SignalStrength signalStrength) {
         if (!checkNotifyPermission("notifySignalStrength()")) {
@@ -1324,6 +1406,8 @@
                 pw.println("  mCallState=" + mCallState[i]);
                 pw.println("  mCallIncomingNumber=" + mCallIncomingNumber[i]);
                 pw.println("  mServiceState=" + mServiceState[i]);
+                pw.println("  mVoiceActivationState= " + mVoiceActivationState[i]);
+                pw.println("  mDataActivationState= " + mDataActivationState[i]);
                 pw.println("  mSignalStrength=" + mSignalStrength[i]);
                 pw.println("  mMessageWaiting=" + mMessageWaiting[i]);
                 pw.println("  mCallForwarding=" + mCallForwarding[i]);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2bc131f..cec5800 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -703,21 +703,27 @@
         return false;
     }
 
-    public void setServiceForegroundLocked(ComponentName className, IBinder token,
+    public long setServiceForegroundLocked(ComponentName className, IBinder token,
             int id, Notification notification, int flags) {
         final int userId = UserHandle.getCallingUserId();
         final long origId = Binder.clearCallingIdentity();
         try {
             ServiceRecord r = findServiceLocked(className, token, userId);
             if (r != null) {
-                setServiceForegroundInnerLocked(r, id, notification, flags);
+                return setServiceForegroundInnerLocked(r, id, notification, flags);
             }
+            return ActivityThread.INVALID_PROC_STATE_SEQ;
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
-    private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
+    /**
+     * @return current process state sequence number {@link UidRecord#curProcStateSeq} corresponding
+     *         to the ServiceRecord {@param r} if the calling service has to block until the
+     *         network rules are udpated, Otherwise {@link ActivityThread#INVALID_PROC_STATE_SEQ}.
+     */
+    private long setServiceForegroundInnerLocked(ServiceRecord r, int id,
             Notification notification, int flags) {
         if (id != 0) {
             if (notification == null) {
@@ -736,7 +742,7 @@
                         Slog.w(TAG, "Instant app " + r.appInfo.packageName
                                 + " does not have permission to create foreground services"
                                 + ", ignoring.");
-                        return;
+                        return ActivityThread.INVALID_PROC_STATE_SEQ;
                     case AppOpsManager.MODE_ERRORED:
                         throw new SecurityException("Instant app " + r.appInfo.packageName
                                 + " does not have permission to create foreground services");
@@ -764,12 +770,18 @@
             r.foregroundNoti = notification;
             r.isForeground = true;
             r.postNotification();
+            long procStateSeqToReturn = ActivityThread.INVALID_PROC_STATE_SEQ;
             if (r.app != null) {
                 updateServiceForegroundLocked(r.app, true);
+                if (r.app.uidRecord != null &&
+                        r.app.uidRecord.blockState == ActivityThread.NETWORK_STATE_BLOCK) {
+                    procStateSeqToReturn = r.app.uidRecord.curProcStateSeq;
+                }
             }
             getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
             mAm.notifyPackageUse(r.serviceInfo.packageName,
                                  PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
+            return procStateSeqToReturn;
         } else {
             if (r.isForeground) {
                 r.isForeground = false;
@@ -790,6 +802,7 @@
                 }
             }
         }
+        return ActivityThread.INVALID_PROC_STATE_SEQ;
     }
 
     private void cancelForegroudNotificationLocked(ServiceRecord r) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 88e0d03..8ed95ee 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -63,6 +63,7 @@
     static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
     static final boolean DEBUG_LRU = DEBUG_ALL || false;
     static final boolean DEBUG_MU = DEBUG_ALL || false;
+    static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
     static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
     static final boolean DEBUG_POWER = DEBUG_ALL || false;
@@ -107,6 +108,7 @@
     static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : "";
     static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : "";
     static final String POSTFIX_MU = "_MU";
+    static final String POSTFIX_NETWORK = "_Network";
     static final String POSTFIX_OOM_ADJ = (APPEND_CATEGORY_NAME) ? "_OomAdj" : "";
     static final String POSTFIX_PAUSE = (APPEND_CATEGORY_NAME) ? "_Pause" : "";
     static final String POSTFIX_POWER = (APPEND_CATEGORY_NAME) ? "_Power" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d95d538..a73eb18 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -42,6 +42,9 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileRestrictBackgroundOn;
+import static android.net.NetworkPolicyManager.UidStateWithSeqObserver;
 import static android.os.Build.VERSION_CODES.N;
 import static android.os.Process.PROC_CHAR;
 import static android.os.Process.PROC_OUT_LONG;
@@ -76,6 +79,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
@@ -105,6 +109,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
@@ -353,6 +358,7 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.firewall.IntentFirewall;
+import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.statusbar.StatusBarManagerInternal;
@@ -409,6 +415,7 @@
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
     private static final String TAG_LRU = TAG + POSTFIX_LRU;
     private static final String TAG_MU = TAG + POSTFIX_MU;
+    private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
     private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
     private static final String TAG_POWER = TAG + POSTFIX_POWER;
     private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
@@ -595,6 +602,8 @@
     BroadcastStats mLastBroadcastStats;
     BroadcastStats mCurBroadcastStats;
 
+    private UidStateWithSeqObserver mUidStateWithSeqObserver;
+
     BroadcastQueue broadcastQueueForIntent(Intent intent) {
         final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
         if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
@@ -4211,6 +4220,25 @@
                     "*** Delivering " + N + " uid changes");
         }
 
+        if (mUidStateWithSeqObserver != null) {
+            final int registeredCallbackCount = mUidObservers.getRegisteredCallbackCount();
+            for (int i = 0; i < N; ++i) {
+                final UidRecord.ChangeItem item = mActiveUidChanges[i];
+                if (item.change == UidRecord.CHANGE_PROCSTATE) {
+                    mUidStateWithSeqObserver.onUidStateChangedWithSeq(
+                            item.uid, item.processState, item.procStateSeq);
+                    if (VALIDATE_UID_STATES && registeredCallbackCount == 0) {
+                        UidRecord validateUid = mValidateUids.get(item.uid);
+                        if (validateUid == null) {
+                            validateUid = new UidRecord(item.uid);
+                            mValidateUids.put(item.uid, validateUid);
+                        }
+                        validateUid.curProcState = validateUid.setProcState = item.processState;
+                    }
+                }
+            }
+        }
+
         int i = mUidObservers.beginBroadcast();
         while (i > 0) {
             i--;
@@ -8459,12 +8487,7 @@
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        if ((callingAppId == Process.SYSTEM_UID) || (callingAppId == Process.ROOT_UID)) {
-            Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
-                    + " grant to " + grantUri + "; use startActivityAsCaller() instead");
-            return -1;
-        } else {
+        if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
             if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
                 // Require they hold a strong enough Uri permission
                 if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
@@ -9600,6 +9623,20 @@
     }
 
     @Override
+    public ActivityManager.TaskDescription getTaskDescription(int id) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                    "getTaskDescription()");
+            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+                    id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+            if (tr != null) {
+                return tr.lastTaskDescription;
+            }
+        }
+        return null;
+    }
+
+    @Override
     public int addAppTask(IBinder activityToken, Intent intent,
             ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
         final int callingUid = Binder.getCallingUid();
@@ -17578,11 +17615,16 @@
         }
     }
 
+    /**
+     * Returns sequence number associated with the current process state change if the service
+     * coming to the foreground needs to block for network before proceeding, otherwise
+     * {@link ActivityThread#INVALID_PROC_STATE_SEQ}.
+     */
     @Override
-    public void setServiceForeground(ComponentName className, IBinder token,
+    public long setServiceForeground(ComponentName className, IBinder token,
             int id, Notification notification, int flags) {
         synchronized(this) {
-            mServices.setServiceForegroundLocked(className, token, id, notification, flags);
+            return mServices.setServiceForegroundLocked(className, token, id, notification, flags);
         }
     }
 
@@ -21343,6 +21385,7 @@
         pendingChange.processState = uidRec != null
                 ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
         pendingChange.ephemeral = uidRec.ephemeral;
+        pendingChange.procStateSeq = uidRec.curProcStateSeq;
 
         // Directly update the power manager, since we sit on top of it and it is critical
         // it be kept in sync (so wake locks will be held as soon as appropriate).
@@ -21716,6 +21759,34 @@
             }
         }
 
+        for (int i = mActiveUids.size() - 1; i >= 0; --i) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            uidRec.shouldNotifyAppThreads = false;
+            if (uidRec.curProcState == uidRec.setProcState) {
+                continue;
+            }
+            final int newBlockState = getUidRecordBlockState(uidRec);
+            // Sequence no. associated with process state change will only be updated if the
+            // process is coming from background to foreground or vice versa.
+            if (newBlockState != uidRec.blockState) {
+                uidRec.blockState = newBlockState;
+                uidRec.curProcStateSeq++;
+                uidRec.appThreadListeners = null;
+                uidRec.shouldNotifyAppThreads = true;
+            }
+        }
+
+        for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
+            final ProcessRecord app = mLruProcesses.get(i);
+            if (!app.killedByAm && app.thread != null && app.uidRecord.shouldNotifyAppThreads) {
+                try {
+                    app.thread.setBlockForNetworkState(app.uidRecord.blockState,
+                            app.uidRecord.curProcStateSeq);
+                } catch (RemoteException ignored) {
+                }
+            }
+        }
+
         mNumServiceProcs = mNewNumServiceProcs;
 
         // Now determine the memory trimming level of background processes.
@@ -22917,6 +22988,131 @@
                 updateOomAdjLocked(pr);
             }
         }
+
+        @Override
+        public void setUidStateWithSeqObserver(UidStateWithSeqObserver observer) {
+            synchronized (ActivityManagerService.this) {
+                mUidStateWithSeqObserver = observer;
+            }
+        }
+
+        @Override
+        public void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq) {
+            if (DEBUG_NETWORK) {
+                Slog.d(TAG_NETWORK, "Got update from NPMS uid: " + uid + " seq: " + procStateSeq);
+            }
+            synchronized (ActivityManagerService.this) {
+                final UidRecord record = mActiveUids.get(uid);
+                if (record == null) {
+                    if (DEBUG_NETWORK) {
+                        Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid
+                                + "seq: " + procStateSeq);
+                    }
+                    return;
+                }
+                record.lastProcStateSeqWithUpdatedNetworkState = procStateSeq;
+                if (record.curProcStateSeq > procStateSeq) {
+                    if (DEBUG_NETWORK) {
+                        Slog.d(TAG_NETWORK, "Since the current procStateSeq is greater, the "
+                                + "listeners would already be notified when it is incremented.");
+                    }
+                    return;
+                }
+
+                if (record.appThreadListeners == null) {
+                    if (DEBUG_NETWORK) {
+                        Slog.d(TAG_NETWORK, "No app thread listeners for uid: " + uid
+                                + "seq: " + procStateSeq);
+                    }
+                    return;
+                }
+                for (int i = record.appThreadListeners.beginBroadcast() - 1; i >= 0; i--) {
+                    final IApplicationThread listener =
+                            record.appThreadListeners.getBroadcastItem(i);
+                    try {
+                        if (listener != null) {
+                            listener.notifyNetworkStateUpdated(procStateSeq);
+                        }
+                    } catch (RemoteException ignored) {
+                    }
+                }
+                record.appThreadListeners.finishBroadcast();
+                record.appThreadListeners = null;
+            }
+        }
+    }
+
+    @Override
+    public boolean registerNetworkRulesUpdateListener(IApplicationThread listener,
+            long procStateSeq) {
+        synchronized (this) {
+            final int uid = Binder.getCallingUid();
+            final UidRecord record = mActiveUids.get(uid);
+            if (record.lastProcStateSeqWithUpdatedNetworkState >= procStateSeq) {
+                if (DEBUG_NETWORK) {
+                    Slog.v(TAG_NETWORK, "Network state is already updated for seq: " + procStateSeq
+                            + ". No need to register listener for uid: " + uid);
+                }
+                return false;
+            }
+            if (record.curProcStateSeq > procStateSeq) {
+                if (DEBUG_NETWORK) {
+                    Slog.v(TAG_NETWORK, "Since the current procState is greater, there is no need "
+                            + " to register listeners for older seq numbers");
+                }
+            }
+            if (record.appThreadListeners == null) {
+                record.appThreadListeners = new RemoteCallbackList<>();
+            }
+            record.appThreadListeners.register(listener);
+            if (DEBUG_NETWORK) {
+                Slog.v(TAG_NETWORK, "Registered listener for uid: " + uid + " seq: " + procStateSeq);
+            }
+            return true;
+        }
+    }
+
+    private int getUidRecordBlockState(UidRecord uidRec) {
+        final boolean curStateAllowedWhileRestrictBackgroundOn
+                = isProcStateAllowedWhileRestrictBackgroundOn(uidRec.curProcState);
+        final boolean curStateAllowedWhileIdleOrPowerSaveMode
+                = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.curProcState);
+
+        if (uidRec.setProcState == ActivityManager.PROCESS_STATE_UNKNOWN) {
+            if (uidRec.curProcState != ActivityManager.PROCESS_STATE_UNKNOWN &&
+                    (curStateAllowedWhileIdleOrPowerSaveMode
+                            || curStateAllowedWhileRestrictBackgroundOn)) {
+                return ActivityThread.NETWORK_STATE_BLOCK;
+            }
+            return ActivityThread.NETWORK_STATE_NO_CHANGE;
+        }
+
+        final boolean prevStateAllowedWhileRestrictBackgroundOn
+                = isProcStateAllowedWhileRestrictBackgroundOn(uidRec.setProcState);
+        final boolean prevStateAllowedWhileIdleOrPowerSaveMode
+                = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState);
+
+        if (prevStateAllowedWhileIdleOrPowerSaveMode == curStateAllowedWhileIdleOrPowerSaveMode &&
+                prevStateAllowedWhileRestrictBackgroundOn ==
+                        curStateAllowedWhileRestrictBackgroundOn) {
+            return uidRec.blockState;
+        }
+
+        if (!prevStateAllowedWhileIdleOrPowerSaveMode && curStateAllowedWhileIdleOrPowerSaveMode) {
+            return ActivityThread.NETWORK_STATE_BLOCK;
+        } else if (!prevStateAllowedWhileRestrictBackgroundOn
+                && curStateAllowedWhileRestrictBackgroundOn) {
+            return ActivityThread.NETWORK_STATE_BLOCK;
+        }
+
+        if (prevStateAllowedWhileIdleOrPowerSaveMode && !curStateAllowedWhileIdleOrPowerSaveMode) {
+            return ActivityThread.NETWORK_STATE_UNBLOCK;
+        } else if (prevStateAllowedWhileRestrictBackgroundOn &&
+                !curStateAllowedWhileRestrictBackgroundOn) {
+            return ActivityThread.NETWORK_STATE_UNBLOCK;
+        }
+
+        return uidRec.blockState;
     }
 
     private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 73a17c6..9b459d1 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -49,6 +49,7 @@
         // add other system settings here...
 
         sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.WAIT_FOR_NETWORK_TIMEOUT_MS, long.class);
         // add other global settings here...
     }
 
@@ -56,6 +57,8 @@
 
     private final ActivityManagerService mActivityManagerService;
 
+    private static final long WAIT_FOR_NETWORK_TIMEOUT_DEFAULT_MS = 2000; // 2 sec
+
     public CoreSettingsObserver(ActivityManagerService activityManagerService) {
         super(activityManagerService.mHandler);
         mActivityManagerService = activityManagerService;
@@ -143,7 +146,13 @@
                 } else if (map == sSystemSettingToTypeMap) {
                     value = Settings.System.getLong(context.getContentResolver(), setting, 0);
                 } else {
-                    value = Settings.Global.getLong(context.getContentResolver(), setting, 0);
+                    // TODO: remove this conditional and set the default in settings provider.
+                    if (Settings.Global.WAIT_FOR_NETWORK_TIMEOUT_MS.equals(setting)) {
+                        value = Settings.Global.getLong(context.getContentResolver(), setting,
+                                WAIT_FOR_NETWORK_TIMEOUT_DEFAULT_MS);
+                    } else {
+                        value = Settings.Global.getLong(context.getContentResolver(), setting, 0);
+                    }
                 }
                 snapshot.putLong(setting, value);
             }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 302f628..1e16bc9 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -17,8 +17,12 @@
 package com.android.server.am;
 
 import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.os.RemoteCallbackList;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.DebugUtils;
 import android.util.TimeUtils;
 
 /**
@@ -34,6 +38,27 @@
     boolean setWhitelist;
     boolean idle;
     int numProcs;
+    /**
+     * Seq no. associated with the current process state change (from background to foreground or
+     * vice versa).
+     */
+    long curProcStateSeq;
+    /**
+     * Latest seq number for which NetworkPolicyManagerService notified ActivityManagerService that
+     * network policy rules are updated.
+     */
+    long lastProcStateSeqWithUpdatedNetworkState;
+    /**
+     * Current block state indicating whether components in the process corresponding to this
+     * uidRecord needs to block for network or unblock or if there is no change.
+     * value will be one of {@link ActivityThread#NETWORK_STATE_BLOCK},
+     * {@link ActivityThread#NETWORK_STATE_UNBLOCK}, {@link ActivityThread#NETWORK_STATE_NO_CHANGE}.
+     */
+    int blockState;
+    /** Indicates whether app threads need be notified of the current blockState change. */
+    boolean shouldNotifyAppThreads;
+    /** Listeners waiting for the network policy rules to get updated. */
+    RemoteCallbackList<IApplicationThread> appThreadListeners;
 
     static final int CHANGE_PROCSTATE = 0;
     static final int CHANGE_GONE = 1;
@@ -47,6 +72,7 @@
         int change;
         int processState;
         boolean ephemeral;
+        long procStateSeq;
     }
 
     ChangeItem pendingChange;
@@ -83,6 +109,14 @@
         }
         sb.append(" procs:");
         sb.append(numProcs);
+        sb.append(" procStateSeq:");
+        sb.append(curProcStateSeq);
+        sb.append(" lastProcStateSeqWithUpdatedNetworkState:");
+        sb.append(lastProcStateSeqWithUpdatedNetworkState);
+        sb.append(" blockState:");
+        sb.append(DebugUtils.valueToString(ActivityThread.class, "NETWORK_STATE_", blockState));
+        sb.append(" shouldNotifyAppThreads:");
+        sb.append(shouldNotifyAppThreads);
         sb.append("}");
         return sb.toString();
     }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9d63462..6c608a2 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1008,10 +1008,9 @@
                 return false;
             }
 
-            protected boolean requestUpstreamMobileConnection() {
+            protected void requestUpstreamMobileConnection() {
                 mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
                 mUpstreamNetworkMonitor.registerMobileNetworkRequest();
-                return true;
             }
 
             protected void unrequestUpstreamMobileConnection() {
@@ -1058,9 +1057,13 @@
             }
 
             protected void chooseUpstreamType(boolean tryCell) {
+                final int upstreamType = findPreferredUpstreamType(tryCell);
+                setUpstreamByType(upstreamType);
+            }
+
+            protected int findPreferredUpstreamType(boolean tryCell) {
                 final ConnectivityManager cm = getConnectivityManager();
                 int upType = ConnectivityManager.TYPE_NONE;
-                String iface = null;
 
                 updateConfiguration(); // TODO - remove?
 
@@ -1100,7 +1103,8 @@
                         requestUpstreamMobileConnection();
                         break;
                     case ConnectivityManager.TYPE_NONE:
-                        if (tryCell && requestUpstreamMobileConnection()) {
+                        if (tryCell) {
+                            requestUpstreamMobileConnection();
                             // We think mobile should be coming up; don't set a retry.
                         } else {
                             sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
@@ -1117,7 +1121,13 @@
                         break;
                 }
 
+                return upType;
+            }
+
+            protected void setUpstreamByType(int upType) {
+                final ConnectivityManager cm = getConnectivityManager();
                 Network network = null;
+                String iface = null;
                 if (upType != ConnectivityManager.TYPE_NONE) {
                     LinkProperties linkProperties = cm.getLinkProperties(upType);
                     if (linkProperties != null) {
@@ -1354,9 +1364,9 @@
                 simChange.startListening();
                 mUpstreamNetworkMonitor.start();
 
-                mTryCell = true;  // better try something first pass or crazy tests cases will fail
-                chooseUpstreamType(mTryCell);
-                mTryCell = !mTryCell;
+                // Better try something first pass or crazy tests cases will fail.
+                chooseUpstreamType(true);
+                mTryCell = false;
             }
 
             @Override
@@ -1407,10 +1417,9 @@
                         break;
                     }
                     case CMD_UPSTREAM_CHANGED:
-                        // need to try DUN immediately if Wifi goes down
-                        mTryCell = true;
-                        chooseUpstreamType(mTryCell);
-                        mTryCell = !mTryCell;
+                        // Need to try DUN immediately if Wi-Fi goes down.
+                        chooseUpstreamType(true);
+                        mTryCell = false;
                         break;
                     case CMD_RETRY_UPSTREAM:
                         chooseUpstreamType(mTryCell);
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 37221a9..5e51579 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -250,31 +250,33 @@
         }
 
         private void cleanupUpstream() {
-            if (mMyUpstreamIfaceName != null) {
-                // note that we don't care about errors here.
-                // sometimes interfaces are gone before we get
-                // to remove their rules, which generates errors.
-                // just do the best we can.
-                try {
-                    // about to tear down NAT; gather remaining statistics
-                    mStatsService.forceUpdate();
-                } catch (Exception e) {
-                    if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
-                }
-                try {
-                    mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
-                } catch (Exception e) {
-                    if (VDBG) Log.e(
-                            TAG, "Exception in removeInterfaceForward: " + e.toString());
-                }
-                try {
-                    mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
-                } catch (Exception e) {
-                    if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
-                }
-                mMyUpstreamIfaceName = null;
+            if (mMyUpstreamIfaceName == null) return;
+
+            cleanupUpstreamInterface(mMyUpstreamIfaceName);
+            mMyUpstreamIfaceName = null;
+        }
+
+        private void cleanupUpstreamInterface(String upstreamIface) {
+            // Note that we don't care about errors here.
+            // Sometimes interfaces are gone before we get
+            // to remove their rules, which generates errors.
+            // Just do the best we can.
+            try {
+                // About to tear down NAT; gather remaining statistics.
+                mStatsService.forceUpdate();
+            } catch (Exception e) {
+                if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
             }
-            return;
+            try {
+                mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface);
+            } catch (Exception e) {
+                if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString());
+            }
+            try {
+                mNMService.disableNat(mIfaceName, upstreamIface);
+            } catch (Exception e) {
+                if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
+            }
         }
 
         @Override
@@ -306,6 +308,7 @@
                                     newUpstreamIfaceName);
                         } catch (Exception e) {
                             Log.e(TAG, "Exception enabling Nat: " + e.toString());
+                            cleanupUpstreamInterface(newUpstreamIfaceName);
                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
                             transitionTo(mInitialState);
                             return true;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index a947b41..5f348bf 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -261,6 +261,7 @@
 
             mPrimaryDisplayDeviceInfo = deviceInfo;
             mInfo = null;
+            mOverrideDisplayInfo = null;
         }
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ac3a025..91c9316 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -23,6 +23,7 @@
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.app.ActivityThread.INVALID_PROC_STATE_SEQ;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.ACTION_USER_ADDED;
@@ -53,11 +54,15 @@
 import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
+import static android.net.NetworkPolicyManager.RULE_INVALID;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.UidStateWithSeqObserver;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileRestrictBackgroundOn;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
@@ -94,6 +99,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
@@ -123,6 +129,7 @@
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
@@ -130,6 +137,7 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
+import android.os.Debug;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -161,8 +169,10 @@
 import android.util.NtpTrustedTime;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TrustedTime;
 import android.util.Xml;
 
@@ -210,14 +220,16 @@
  * enforcement.
  *
  * <p>
- * This class uses 2-3 locks to synchronize state:
+ * This class uses 4 locks to synchronize state:
  * <ul>
  * <li>{@code mUidRulesFirstLock}: used to guard state related to individual UIDs (such as firewall
  * rules).
  * <li>{@code mNetworkPoliciesSecondLock}: used to guard state related to network interfaces (such
  * as network policies).
- * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that all locks
- * must be held.
+ * <li>{@code mDispatchedThirdLock}: used to guard state related to process state sequence numbers
+ * of uids which are currently blocked waiting for network.
+ * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that both locks
+ * {@code mUidRulesFirstLock} and {@code mNetworkPoliciesSecondLock} must be held.
  * </ul>
  *
  * <p>
@@ -225,8 +237,11 @@
  * <ul>
  * <li>{@code UL()}: require the "UID" lock ({@code mUidRulesFirstLock}).
  * <li>{@code NL()}: require the "Network" lock ({@code mNetworkPoliciesSecondLock}).
- * <li>{@code AL()}: require all locks, which must be obtained in order ({@code mUidRulesFirstLock}
- * first, then {@code mNetworkPoliciesSecondLock}, then {@code mYetAnotherGuardThirdLock}, etc..
+ * <li>{@code DL()}: require the "Dispatched" lock ({@code mDispatchedThirdLock}).
+ * <li>{@code AL()}: require both locks {@code mUidRulesFirstLock} and
+ * {@code mNetworkPoliciesSecondLock}.
+ * When multiple locks are needed, they must be obtained in order ({@code mUidRulesFirstLock}
+ * first, then {@code mNetworkPoliciesSecondLock}, then {@code mDispatchedThirdLock}, etc..
  * </ul>
  */
 public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@@ -313,6 +328,27 @@
     // See main javadoc for instructions on how to use these locks.
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
+    final Object mDispatchedThirdLock = new Object();
+
+    @GuardedBy("mDispatchedThirdLock")
+    private final SparseLongArray mLastHandledProcStateSeq = new SparseLongArray();
+
+    /**
+     * Used for tracking whether the updated uid and firewall rules have been dispatched to
+     * ConnectivityService and NetworkManagementService respectively.
+     *
+     * SparseIntArray: uid -> dispatch flags (one or more combinations of {@link #FLAG_NONE},
+     * {@link #FLAG_UID_RULES_DISPATCHED}, {@link #FLAG_FIREWALL_RULES_DISPATCHED} and
+     * {@link #FLAG_ALL_RULES_DISPATCHED}).
+     */
+    @GuardedBy("mDispatchedThirdLock")
+    private final SparseIntArray mDispatchFlagsForCurProcStateSeq = new SparseIntArray();
+
+    private final int FLAG_NONE = 0;
+    private final int FLAG_UID_RULES_DISPATCHED = 1 << 0;
+    private final int FLAG_FIREWALL_RULES_DISPATCHED = 1 << 1;
+    private final int FLAG_ALL_RULES_DISPATCHED =
+            (FLAG_UID_RULES_DISPATCHED | FLAG_FIREWALL_RULES_DISPATCHED);
 
     @GuardedBy("allLocks") volatile boolean mSystemReady;
 
@@ -407,6 +443,8 @@
 
     private final IPackageManager mIPm;
 
+    private ActivityManagerInternal mActivityManagerInternal;
+
 
     // TODO: keep whitelist of system-critical services that should never have
     // rules enforced, such as system, phone, and radio UIDs.
@@ -617,13 +655,16 @@
 
             try {
                 mActivityManager.registerUidObserver(mUidObserver,
-                        ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE,
+                        ActivityManager.UID_OBSERVER_GONE,
                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
                 mNetworkManager.registerObserver(mAlertObserver);
             } catch (RemoteException e) {
                 // ignored; both services live in system_server
             }
 
+            mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+            mActivityManagerInternal.setUidStateWithSeqObserver(mUidStateWithSeqObserver);
+
             // listen for changes to power save whitelist
             final IntentFilter whitelistFilter = new IntentFilter(
                     PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
@@ -705,17 +746,24 @@
         }
     }
 
-    final private IUidObserver mUidObserver = new IUidObserver.Stub() {
-        @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+    final private UidStateWithSeqObserver mUidStateWithSeqObserver = new UidStateWithSeqObserver() {
+        @Override
+        public void onUidStateChangedWithSeq(int uid, int procState, long procStateSeq) {
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
             try {
+                final long effectiveProcStateSeq = getEffectiveProcStateSeq(uid, procStateSeq);
                 synchronized (mUidRulesFirstLock) {
-                    updateUidStateUL(uid, procState);
+                    updateUidStateUL(uid, procState, effectiveProcStateSeq);
                 }
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
             }
         }
+    };
+
+    final private IUidObserver mUidObserver = new IUidObserver.Stub() {
+        @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+        }
 
         @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
             synchronized (mUidRulesFirstLock) {
@@ -1842,7 +1890,7 @@
         }
 
         // uid policy changed, recompute rules and persist policy.
-        updateRulesForDataUsageRestrictionsUL(uid);
+        updateRulesForDataUsageRestrictionsUL(uid, true);
         if (persist) {
             synchronized (mNetworkPoliciesSecondLock) {
                 writePolicyAL();
@@ -2441,7 +2489,7 @@
 
     private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
         final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
-        return isProcStateAllowedWhileOnRestrictBackground(procState);
+        return isProcStateAllowedWhileRestrictBackgroundOn(procState);
     }
 
     private boolean isUidForegroundOnRestrictPowerUL(int uid) {
@@ -2459,26 +2507,50 @@
      * {@link #updateRulesForDataUsageRestrictionsUL(int)} and
      * {@link #updateRulesForPowerRestrictionsUL(int)}
      */
-    private void updateUidStateUL(int uid, int uidState) {
+    private void updateUidStateUL(int uid, int uidState, long procStateSeq) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
         try {
             final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
             if (oldUidState != uidState) {
                 // state changed, push updated rules
                 mUidState.put(uid, uidState);
-                updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, uidState);
-                if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
-                        != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
-                    if (isUidIdle(uid)) {
-                        updateRuleForAppIdleUL(uid);
+                if (procStateSeq != INVALID_PROC_STATE_SEQ) {
+                    int updatedUidRules = RULE_INVALID;
+                    ReturnStatus status = updateRestrictBackgroundRulesOnUidStatusChangedUL(
+                            uid, oldUidState, uidState, false);
+                    if (status != null && status.mNeedToNotify) {
+                        updatedUidRules = status.mNewUidRules;
                     }
-                    if (mDeviceIdleMode) {
-                        updateRuleForDeviceIdleUL(uid);
+                    final boolean procStateChangedAllowedWhileIdleOrPowerSaveMode =
+                            isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
+                                    != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState);
+                    if (procStateChangedAllowedWhileIdleOrPowerSaveMode) {
+                        status = updateRulesForPowerRestrictionsUL(uid, false);
+                        if (status != null && status.mNeedToNotify) {
+                            updatedUidRules = status.mNewUidRules;
+                        }
                     }
-                    if (mRestrictPower) {
-                        updateRuleForRestrictPowerUL(uid);
+                    // TODO: We can avoid this if the rules are not changed. But since dispatching
+                    // to ConnectivityService is currently asynchronous, we need this to make sure
+                    // any previous the msg_rules_changes have been handled. Optimize this once
+                    // dispatching from NPMS to ConnectivityService is made synchronous.
+                    mHandler.obtainMessage(MSG_RULES_CHANGED, uid, updatedUidRules,
+                            procStateSeq).sendToTarget();
+                    if (procStateChangedAllowedWhileIdleOrPowerSaveMode) {
+                        if (isUidIdle(uid)) {
+                            updateRuleForAppIdleUL(uid);
+                        }
+                        if (mDeviceIdleMode) {
+                            updateRuleForDeviceIdleUL(uid);
+                        }
+                        if (mRestrictPower) {
+                            updateRuleForRestrictPowerUL(uid);
+                        }
                     }
-                    updateRulesForPowerRestrictionsUL(uid);
+                    synchronized (mDispatchedThirdLock) {
+                        setDispatchedFlagDL(uid, procStateSeq, FLAG_FIREWALL_RULES_DISPATCHED);
+                        checkAndNotifyDL(uid, procStateSeq);
+                    }
                 }
                 updateNetworkStats(uid, isUidStateForegroundUL(uidState));
             }
@@ -2487,6 +2559,61 @@
         }
     }
 
+    /**
+     * Returns {@link android.app.ActivityThread#INVALID_PROC_STATE_SEQ} if acting on
+     * {@param procStateSeq} leads to an invalid state, otherwise update global state and return
+     * {@param procStateSeq}.
+     */
+    private long getEffectiveProcStateSeq(int uid, long procStateSeq) {
+        synchronized (mDispatchedThirdLock) {
+            final long lastHandledProcStateSeq = mLastHandledProcStateSeq.get(uid);
+            if (procStateSeq < lastHandledProcStateSeq) {
+                Slog.wtf(TAG, "procStateSeq from AMS should never go down, procStateSeq: "
+                        + procStateSeq + " lastHandledProcStateSeq: " + lastHandledProcStateSeq
+                        + " uid: " + uid);
+                return INVALID_PROC_STATE_SEQ;
+            }
+            if (procStateSeq == lastHandledProcStateSeq) {
+                if (LOGD) {
+                    Slog.d(TAG, "procStateSeq: " + procStateSeq + " is not changed, so process is "
+                            + "not jumping from background to foreground or vice versa. "
+                            + "uid: " + uid);
+                }
+                return INVALID_PROC_STATE_SEQ;
+            }
+            mLastHandledProcStateSeq.put(uid, procStateSeq);
+            mDispatchFlagsForCurProcStateSeq.put(uid, 0);
+            return procStateSeq;
+        }
+    }
+
+    /**
+     * Update dispatch flags to include {@param flag}.
+     */
+    private void setDispatchedFlagDL(int uid, long procStateSeq, int flag) {
+        int dispatchedFlag = mDispatchFlagsForCurProcStateSeq.get(uid);
+        dispatchedFlag |= flag;
+        mDispatchFlagsForCurProcStateSeq.put(uid, dispatchedFlag);
+    }
+
+    /**
+     * Check whether uid and firewall rules are dispatched to ConnectivityService and
+     * NetworkManagementService respectively, if so notify ActivityManagerService that network
+     * rules are updated.
+     */
+    private void checkAndNotifyDL(int uid, long procStateSeq) {
+        synchronized (mDispatchedThirdLock) {
+            final int dispatchedFlags = mDispatchFlagsForCurProcStateSeq.get(uid);
+            if (dispatchedFlags == FLAG_ALL_RULES_DISPATCHED) {
+                if (LOGD) {
+                    Slog.d(TAG, "Notifying AMS that network rules are updated for uid: " + uid
+                            + " seq: " + procStateSeq + " callers: " + Debug.getCallers(3));
+                }
+                mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
+            }
+        }
+    }
+
     private void removeUidStateUL(int uid) {
         final int index = mUidState.indexOfKey(uid);
         if (index >= 0) {
@@ -2494,17 +2621,21 @@
             mUidState.removeAt(index);
             if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
                 updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState,
-                        ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+                        ActivityManager.PROCESS_STATE_CACHED_EMPTY, true);
                 if (mDeviceIdleMode) {
                     updateRuleForDeviceIdleUL(uid);
                 }
                 if (mRestrictPower) {
                     updateRuleForRestrictPowerUL(uid);
                 }
-                updateRulesForPowerRestrictionsUL(uid);
+                updateRulesForPowerRestrictionsUL(uid, true);
                 updateNetworkStats(uid, false);
             }
         }
+        synchronized (mDispatchedThirdLock) {
+            mLastHandledProcStateSeq.delete(uid);
+            mDispatchFlagsForCurProcStateSeq.delete(uid);
+        }
     }
 
     // adjust stats accounting based on foreground status
@@ -2516,23 +2647,16 @@
         }
     }
 
-    private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState,
-            int newUidState) {
+    private ReturnStatus updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState,
+            int newUidState, boolean notify) {
         final boolean oldForeground =
-                isProcStateAllowedWhileOnRestrictBackground(oldUidState);
+                isProcStateAllowedWhileRestrictBackgroundOn(oldUidState);
         final boolean newForeground =
-                isProcStateAllowedWhileOnRestrictBackground(newUidState);
+                isProcStateAllowedWhileRestrictBackgroundOn(newUidState);
         if (oldForeground != newForeground) {
-            updateRulesForDataUsageRestrictionsUL(uid);
+            return updateRulesForDataUsageRestrictionsUL(uid, notify);
         }
-    }
-
-    static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
-        return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-    }
-
-    static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
-        return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+        return null;
     }
 
     void updateRulesForPowerSaveUL() {
@@ -2681,7 +2805,7 @@
                 // Skip if it had no restrictions to begin with
                 if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
             }
-            updateRulesForPowerRestrictionsUL(uid, oldRules, paroled);
+            updateRulesForPowerRestrictionsUL(uid, oldRules, paroled, true);
         }
     }
 
@@ -2761,10 +2885,10 @@
                     final int uid = UserHandle.getUid(user.id, app.uid);
                     switch (type) {
                         case TYPE_RESTRICT_BACKGROUND:
-                            updateRulesForDataUsageRestrictionsUL(uid);
+                            updateRulesForDataUsageRestrictionsUL(uid, true);
                             break;
                         case TYPE_RESTRICT_POWER:
-                            updateRulesForPowerRestrictionsUL(uid);
+                            updateRulesForPowerRestrictionsUL(uid, true);
                             break;
                         default:
                             Slog.w(TAG, "Invalid type for updateRulesForAllApps: " + type);
@@ -2790,7 +2914,7 @@
                 updateRuleForDeviceIdleUL(uid);
                 updateRuleForRestrictPowerUL(uid);
                 // Update internal rules.
-                updateRulesForPowerRestrictionsUL(uid);
+                updateRulesForPowerRestrictionsUL(uid, true);
             }
         }
     }
@@ -2854,6 +2978,10 @@
         mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
         mPowerSaveWhitelistAppIds.delete(uid);
         mPowerSaveTempWhitelistAppIds.delete(uid);
+        synchronized (mDispatchedThirdLock) {
+            mLastHandledProcStateSeq.delete(uid);
+            mDispatchFlagsForCurProcStateSeq.delete(uid);
+        }
 
         // ...then update iptables asynchronously.
         mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -2879,10 +3007,10 @@
         updateRuleForRestrictPowerUL(uid);
 
         // Update internal state for power-related modes.
-        updateRulesForPowerRestrictionsUL(uid);
+        updateRulesForPowerRestrictionsUL(uid, true);
 
         // Update firewall and internal rules for Data Saver Mode.
-        updateRulesForDataUsageRestrictionsUL(uid);
+        updateRulesForDataUsageRestrictionsUL(uid, true);
     }
 
     /**
@@ -2923,11 +3051,16 @@
      *
      * <p>The {@link #mUidRules} map is used to define the transtion of states of an UID.
      *
+     * @param uid The uid for which the rules have to be updated.
+     * @param notify Indicates whether to notify network policy listeners if the rules are updated.
+     *
+     * @return ReturnStatus includes new updated rules and whether network policy listeners
+     *         (INetworkPolicyListener) need to be notified.
      */
-    private void updateRulesForDataUsageRestrictionsUL(int uid) {
+    private ReturnStatus updateRulesForDataUsageRestrictionsUL(int uid, boolean notify) {
         if (!isUidValidForWhitelistRules(uid)) {
             if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid);
-            return;
+            return new ReturnStatus(false, RULE_NONE);
         }
 
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
@@ -3022,9 +3155,12 @@
                         + ", oldRule=" + uidRulesToString(oldUidRules));
             }
 
-            // Dispatch changed rule to existing listeners.
-            mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+            if (notify) {
+                mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+            }
+            return new ReturnStatus(true, newUidRules);
         }
+        return new ReturnStatus(false, newUidRules);
     }
 
     /**
@@ -3045,16 +3181,18 @@
      * <p>
      * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
      */
-    private void updateRulesForPowerRestrictionsUL(int uid) {
+    private ReturnStatus updateRulesForPowerRestrictionsUL(int uid, boolean notify) {
         final int oldUidRules = mUidRules.get(uid, RULE_NONE);
 
-        final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false);
+        final ReturnStatus status = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false,
+                notify);
 
-        if (newUidRules == RULE_NONE) {
+        if (status.mNewUidRules == RULE_NONE) {
             mUidRules.delete(uid);
         } else {
-            mUidRules.put(uid, newUidRules);
+            mUidRules.put(uid, status.mNewUidRules);
         }
+        return status;
     }
 
     /**
@@ -3063,13 +3201,17 @@
      * @param uid the uid of the app to update rules for
      * @param oldUidRules the current rules for the uid, in order to determine if there's a change
      * @param paroled whether to ignore idle state of apps and only look at other restrictions.
+     * @param notify whether to notify network policy listeners (INetworkPolicyListener) if the
+     *               rules are updated.
      *
-     * @return the new computed rules for the uid
+     * @return ReturnStatus includes new updated rules and whether network policy listeners
+     *         (INetworkPolicyListener) need to be notified.
      */
-    private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) {
+    private ReturnStatus updateRulesForPowerRestrictionsUL(int uid, int oldUidRules,
+            boolean paroled, boolean notify) {
         if (!isUidValidForBlacklistRules(uid)) {
             if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
-            return RULE_NONE;
+            return new ReturnStatus(false, RULE_NONE);
         }
 
         final boolean isIdle = !paroled && isUidIdle(uid);
@@ -3121,10 +3263,23 @@
                         + ", newRule=" + uidRulesToString(newUidRules)
                         + ", oldRule=" + uidRulesToString(oldUidRules));
             }
-            mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+            if (notify) {
+                mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+            }
+            return new ReturnStatus(true, newUidRules);
         }
 
-        return newUidRules;
+        return new ReturnStatus(false, newUidRules);
+    }
+
+    private static final class ReturnStatus {
+        boolean mNeedToNotify;
+        int mNewUidRules;
+
+        ReturnStatus(boolean needToNotify, int newUidRules) {
+            mNeedToNotify = needToNotify;
+            mNewUidRules = newUidRules;
+        }
     }
 
     private class AppIdleStateChangeListener
@@ -3138,7 +3293,7 @@
                 if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle);
                 synchronized (mUidRulesFirstLock) {
                     updateRuleForAppIdleUL(uid);
-                    updateRulesForPowerRestrictionsUL(uid);
+                    updateRulesForPowerRestrictionsUL(uid, true);
                 }
             } catch (NameNotFoundException nnfe) {
             }
@@ -3198,13 +3353,26 @@
                 case MSG_RULES_CHANGED: {
                     final int uid = msg.arg1;
                     final int uidRules = msg.arg2;
-                    dispatchUidRulesChanged(mConnectivityListener, uid, uidRules);
-                    final int length = mListeners.beginBroadcast();
-                    for (int i = 0; i < length; i++) {
-                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        dispatchUidRulesChanged(listener, uid, uidRules);
+                    if (uidRules != RULE_INVALID) {
+                        dispatchUidRulesChanged(mConnectivityListener, uid, uidRules);
                     }
-                    mListeners.finishBroadcast();
+                    final Long procStateSeq = (Long) msg.obj;
+                    if (procStateSeq != null) {
+                        synchronized (mDispatchedThirdLock) {
+                            if (mLastHandledProcStateSeq.get(uid) == procStateSeq) {
+                                setDispatchedFlagDL(uid, procStateSeq, FLAG_UID_RULES_DISPATCHED);
+                                checkAndNotifyDL(uid, procStateSeq);
+                            }
+                        }
+                    }
+                    if (uidRules != RULE_INVALID) {
+                        final int length = mListeners.beginBroadcast();
+                        for (int i = 0; i < length; i++) {
+                            final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                            dispatchUidRulesChanged(listener, uid, uidRules);
+                        }
+                        mListeners.finishBroadcast();
+                    }
                     return true;
                 }
                 case MSG_METERED_IFACES_CHANGED: {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 19c9d9b..771ae9a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2458,12 +2458,14 @@
                 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
                 return null;
             }
-            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            try {
-                writePolicyXml(baos, true /*forBackup*/);
-                return baos.toByteArray();
-            } catch (IOException e) {
-                Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+            synchronized(mPolicyFile) {
+                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                try {
+                    writePolicyXml(baos, true /*forBackup*/);
+                    return baos.toByteArray();
+                } catch (IOException e) {
+                    Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+                }
             }
             return null;
         }
@@ -2481,12 +2483,14 @@
                 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
                 return;
             }
-            final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
-            try {
-                readPolicyXml(bais, true /*forRestore*/);
-                savePolicyFile();
-            } catch (NumberFormatException | XmlPullParserException | IOException e) {
-                Slog.w(TAG, "applyRestore: error reading payload", e);
+            synchronized(mPolicyFile) {
+                final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+                try {
+                    readPolicyXml(bais, true /*forRestore*/);
+                    savePolicyFile();
+                } catch (NumberFormatException | XmlPullParserException | IOException e) {
+                    Slog.w(TAG, "applyRestore: error reading payload", e);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 179f310..96a2577 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -515,9 +515,6 @@
             if (!canAccessProfile(callingPackage, targetUserId, "Cannot start activity")) {
                 return false;
             }
-            if (!canAccessProfile(callingPackage, targetUserId, "Cannot access shortcuts")) {
-                return false;
-            }
             if (!isUserEnabled(targetUserId)) {
                 throw new IllegalStateException("Cannot start a shortcut for disabled profile "
                         + targetUserId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ebd0b34..0d63f72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -126,6 +126,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AppsQueryHelper;
+import android.content.pm.ChangedPackages;
 import android.content.pm.ComponentInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.EphemeralRequest;
@@ -720,6 +721,21 @@
 
     private final InstantAppRegistry mInstantAppRegistry;
 
+    @GuardedBy("mPackages")
+    int mChangedPackagesSequenceNumber;
+    /**
+     * List of changed [installed, removed or updated] packages.
+     * mapping from user id -> sequence number -> package name
+     */
+    @GuardedBy("mPackages")
+    final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>();
+    /**
+     * The sequence number of the last change to a package.
+     * mapping from user id -> package name -> sequence number
+     */
+    @GuardedBy("mPackages")
+    final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
+
     public static final class SharedLibraryEntry {
         public final String path;
         public final String apk;
@@ -2141,6 +2157,7 @@
                     pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
                 }
             }
+            scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM);
         }
     }
 
@@ -4220,6 +4237,52 @@
         }
     }
 
+    private void updateSequenceNumberLP(String packageName, int[] userList) {
+        for (int i = userList.length - 1; i >= 0; --i) {
+            final int userId = userList[i];
+            SparseArray<String> changedPackages = mChangedPackages.get(userId);
+            if (changedPackages == null) {
+                changedPackages = new SparseArray<>();
+                mChangedPackages.put(userId, changedPackages);
+            }
+            Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
+            if (sequenceNumbers == null) {
+                sequenceNumbers = new HashMap<>();
+                mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
+            }
+            final Integer sequenceNumber = sequenceNumbers.get(packageName);
+            if (sequenceNumber != null) {
+                changedPackages.remove(sequenceNumber);
+            }
+            changedPackages.put(mChangedPackagesSequenceNumber, packageName);
+            sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber);
+        }
+        mChangedPackagesSequenceNumber++;
+    }
+
+    @Override
+    public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
+        synchronized (mPackages) {
+            if (sequenceNumber >= mChangedPackagesSequenceNumber) {
+                return null;
+            }
+            final SparseArray<String> changedPackages = mChangedPackages.get(userId);
+            if (changedPackages == null) {
+                return null;
+            }
+            final List<String> packageNames =
+                    new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
+            for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
+                final String packageName = changedPackages.get(i);
+                if (packageName != null) {
+                    packageNames.add(packageName);
+                }
+            }
+            return packageNames.isEmpty()
+                    ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
+        }
+    }
+
     @Override
     public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
         ArrayList<FeatureInfo> res;
@@ -13251,6 +13314,7 @@
                     pkgSetting.setHidden(false, userId);
                     pkgSetting.setInstallReason(installReason, userId);
                     mSettings.writePackageRestrictionsLPr(userId);
+                    mSettings.writeKernelMappingLPr(pkgSetting);
                     installed = true;
                 }
             }
@@ -13263,6 +13327,9 @@
                     }
                 }
                 sendPackageAddedForUser(packageName, pkgSetting, userId);
+                synchronized (mPackages) {
+                    updateSequenceNumberLP(packageName, new int[]{ userId });
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -16345,6 +16412,7 @@
             //note that the new package setting would have already been
             //added to mPackages. It hasn't been persisted yet.
             mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
+            // TODO: Remove this write? It's also written at the end of this method
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
             mSettings.writeLPr();
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -16418,6 +16486,7 @@
                 } else if (!previousUserIds.contains(userId)) {
                     ps.setInstallReason(installReason, userId);
                 }
+                mSettings.writeKernelMappingLPr(ps);
             }
             res.name = pkgName;
             res.uid = newPackage.applicationInfo.uid;
@@ -16847,6 +16916,10 @@
                             sUserManager.getUserIds(), true);
                 }
             }
+
+            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                updateSequenceNumberLP(pkgName, res.newUsers);
+            }
         }
     }
 
@@ -17429,6 +17502,7 @@
                 if (res) {
                     mInstantAppRegistry.onPackageUninstalledLPw(uninstalledPs.pkg,
                             info.removedUsers);
+                    updateSequenceNumberLP(packageName, info.removedUsers);
                 }
             }
         }
@@ -17597,6 +17671,7 @@
 
         // writer
         synchronized (mPackages) {
+            boolean installedStateChanged = false;
             if (deletedPs != null) {
                 if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
                     clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
@@ -17644,6 +17719,9 @@
                         if (DEBUG_REMOVE) {
                             Slog.d(TAG, "    user " + userId + " => " + installed);
                         }
+                        if (installed != ps.getInstalled(userId)) {
+                            installedStateChanged = true;
+                        }
                         ps.setInstalled(installed, userId);
                     }
                 }
@@ -17653,6 +17731,9 @@
                 // Save settings now
                 mSettings.writeLPr();
             }
+            if (installedStateChanged) {
+                mSettings.writeKernelMappingLPr(ps);
+            }
         }
         if (removedAppId != -1) {
             // A user ID was deleted here. Go through all users and remove it
@@ -17795,6 +17876,7 @@
                     UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
 
             if (applyUserRestrictions) {
+                boolean installedStateChanged = false;
                 if (DEBUG_REMOVE) {
                     Slog.d(TAG, "Propagating install state across reinstall");
                 }
@@ -17803,6 +17885,9 @@
                     if (DEBUG_REMOVE) {
                         Slog.d(TAG, "    user " + userId + " => " + installed);
                     }
+                    if (installed != ps.getInstalled(userId)) {
+                        installedStateChanged = true;
+                    }
                     ps.setInstalled(installed, userId);
 
                     mSettings.writeRuntimePermissionsForUserLPr(userId, false);
@@ -17810,6 +17895,9 @@
                 // Regardless of writeSettings we need to ensure that this restriction
                 // state propagation is persisted
                 mSettings.writeAllUsersPackageRestrictionsLPr();
+                if (installedStateChanged) {
+                    mSettings.writeKernelMappingLPr(ps);
+                }
             }
             // can downgrade to reader here
             if (writeSettings) {
@@ -18013,6 +18101,7 @@
                     // broadcasts will be sent correctly.
                     if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
                     ps.setInstalled(true, user.getIdentifier());
+                    mSettings.writeKernelMappingLPr(ps);
                 }
             } else {
                 // This is a system app, so we assume that the
@@ -18122,6 +18211,7 @@
                     ps.readUserState(nextUserId).domainVerificationStatus, 0,
                     PackageManager.INSTALL_REASON_UNKNOWN);
         }
+        mSettings.writeKernelMappingLPr(ps);
     }
 
     private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId,
@@ -19725,6 +19815,7 @@
                 }
             }
             scheduleWritePackageRestrictionsLocked(userId);
+            updateSequenceNumberLP(packageName, new int[] { userId });
             components = mPendingBroadcasts.get(userId, packageName);
             final boolean newPackage = components == null;
             if (newPackage) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index b63edfd..0e11b0c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -40,6 +40,9 @@
  * Settings base class for pending and resolved classes.
  */
 abstract class PackageSettingBase extends SettingBase {
+
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
     /**
      * Indicates the state of installation. Used by PackageManager to figure out
      * incomplete installations. Say a package is being installed (the state is
@@ -502,6 +505,25 @@
         userState.delete(userId);
     }
 
+    public int[] getNotInstalledUserIds() {
+        int count = 0;
+        int userStateCount = userState.size();
+        for (int i = 0; i < userStateCount; i++) {
+            if (userState.valueAt(i).installed == false) {
+                count++;
+            }
+        }
+        if (count == 0) return EMPTY_INT_ARRAY;
+        int[] excludedUserIds = new int[count];
+        int idx = 0;
+        for (int i = 0; i < userStateCount; i++) {
+            if (userState.valueAt(i).installed == false) {
+                excludedUserIds[idx++] = userState.keyAt(i);
+            }
+        }
+        return excludedUserIds;
+    }
+
     IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
         return verificationInfo;
     }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 2781150..4814a74 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -72,6 +72,12 @@
     // Append ephemeral to existing seinfo label
     private static final String EPHEMERAL_APP_STR = ":ephemeralapp";
 
+    // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
+    private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
+
+    // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
+    private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
+
     /**
      * Load the mac_permissions.xml file containing all seinfo assignments used to
      * label apps. The loaded mac_permissions.xml file is determined by the
@@ -296,6 +302,8 @@
         if (pkg.applicationInfo.isPrivilegedApp())
             pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR;
 
+        pkg.applicationInfo.seinfo += TARGETSDKVERSION_STR + pkg.applicationInfo.targetSdkVersion;
+
         if (DEBUG_POLICY_INSTALL) {
             Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
                     "seinfo=" + pkg.applicationInfo.seinfo);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 281e445..6156802 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -252,6 +252,7 @@
     private final File mPackageListFilename;
     private final File mStoppedPackagesFilename;
     private final File mBackupStoppedPackagesFilename;
+    /** The top level directory in configfs for sdcardfs to push the package->uid,userId mappings */
     private final File mKernelMappingFilename;
 
     /** Map from package name to settings */
@@ -260,8 +261,8 @@
     /** List of packages that installed other packages */
     final ArraySet<String> mInstallerPackages = new ArraySet<>();
 
-    /** Map from package name to appId */
-    private final ArrayMap<String, Integer> mKernelMapping = new ArrayMap<>();
+    /** Map from package name to appId and excluded userids */
+    private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
 
     // List of replaced system applications
     private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
@@ -271,6 +272,11 @@
     private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
             new ArrayMap<String, IntentFilterVerificationInfo>();
 
+    private static final class KernelPackageState {
+        int appId;
+        int[] excludedUserIds;
+    }
+
     // Bookkeeping for restored user permission grants
     final class RestoredPermissionGrant {
         String permissionName;
@@ -2512,6 +2518,15 @@
         //Debug.stopMethodTracing();
     }
 
+    private void writeKernelRemoveUserLPr(int userId) {
+        if (mKernelMappingFilename == null) return;
+
+        File removeUserIdFile = new File(mKernelMappingFilename, "remove_userid");
+        if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + userId + " to " + removeUserIdFile
+                .getAbsolutePath());
+        writeIntToFile(removeUserIdFile, userId);
+    }
+
     void writeKernelMappingLPr() {
         if (mKernelMappingFilename == null) return;
 
@@ -2538,27 +2553,63 @@
     }
 
     void writeKernelMappingLPr(PackageSetting ps) {
-        if (mKernelMappingFilename == null) return;
+        if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
 
-        final Integer cur = mKernelMapping.get(ps.name);
-        if (cur != null && cur.intValue() == ps.appId) {
-            // Ignore when mapping already matches
-            return;
+        KernelPackageState cur = mKernelMapping.get(ps.name);
+        final boolean firstTime = cur == null;
+        int[] excludedUserIds = ps.getNotInstalledUserIds();
+        final boolean userIdsChanged = firstTime
+                || !Arrays.equals(excludedUserIds, cur.excludedUserIds);
+
+        // Package directory
+        final File dir = new File(mKernelMappingFilename, ps.name);
+
+        if (firstTime) {
+            dir.mkdir();
+            // Create a new mapping state
+            cur = new KernelPackageState();
+            mKernelMapping.put(ps.name, cur);
         }
 
-        if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
+        // If mapping is incorrect or non-existent, write the appid file
+        if (cur.appId != ps.appId) {
+            final File appIdFile = new File(dir, "appid");
+            writeIntToFile(appIdFile, ps.appId);
+            if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
+        }
 
-        final File dir = new File(mKernelMappingFilename, ps.name);
-        dir.mkdir();
+        if (userIdsChanged) {
+            // Build the exclusion list -- the ids to add to the exclusion list
+            for (int i = 0; i < excludedUserIds.length; i++) {
+                if (cur.excludedUserIds == null || !ArrayUtils.contains(cur.excludedUserIds,
+                        excludedUserIds[i])) {
+                    writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]);
+                    if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to "
+                            + ps.name + "/excluded_userids");
+                }
+            }
+            // Build the inclusion list -- the ids to remove from the exclusion list
+            if (cur.excludedUserIds != null) {
+                for (int i = 0; i < cur.excludedUserIds.length; i++) {
+                    if (!ArrayUtils.contains(excludedUserIds, cur.excludedUserIds[i])) {
+                        writeIntToFile(new File(dir, "clear_userid"),
+                                cur.excludedUserIds[i]);
+                        if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to "
+                                + ps.name + "/clear_userid");
 
-        final File file = new File(dir, "appid");
+                    }
+                }
+            }
+            cur.excludedUserIds = excludedUserIds;
+        }
+    }
+
+    private void writeIntToFile(File file, int value) {
         try {
-            // Note that the use of US_ASCII here is safe, we're only writing a decimal
-            // number to the file.
             FileUtils.bytesToFile(file.getAbsolutePath(),
-                    Integer.toString(ps.appId).getBytes(StandardCharsets.US_ASCII));
-            mKernelMapping.put(ps.name, ps.appId);
+                    Integer.toString(value).getBytes(StandardCharsets.US_ASCII));
         } catch (IOException ignored) {
+            Slog.w(TAG, "Couldn't write " + value + " to " + file.getAbsolutePath());
         }
     }
 
@@ -4081,6 +4132,9 @@
                         !ArrayUtils.contains(disallowedPackages, ps.name);
                 // Only system apps are initially installed.
                 ps.setInstalled(shouldInstall, userHandle);
+                if (!shouldInstall) {
+                    writeKernelMappingLPr(ps);
+                }
                 // Need to create a data directory for all apps under this user. Accumulate all
                 // required args and call the installer after mPackages lock has been released
                 volumeUuids[i] = ps.volumeUuid;
@@ -4123,6 +4177,10 @@
         mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
 
         writePackageListLPr();
+
+        // Inform kernel that the user was removed, so that packages are marked uninstalled
+        // for sdcardfs
+        writeKernelRemoveUserLPr(userId);
     }
 
     void removeCrossProfileIntentFiltersLPw(int userId) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2c315445..8f38be8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -919,6 +919,10 @@
     void updateDisplayInfo() {
         mDisplay.getDisplayInfo(mDisplayInfo);
         mDisplay.getMetrics(mDisplayMetrics);
+
+        // Check if display metrics changed and update base values if needed.
+        updateBaseDisplayMetricsIfNeeded();
+
         for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
             mTaskStackContainers.get(i).updateDisplayInfo(null);
         }
@@ -934,10 +938,8 @@
             }
         }
 
-        mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
-        mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
-        mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
-        mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+        updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
+                mDisplayInfo.logicalDensityDpi);
     }
 
     void getLogicalDisplayRect(Rect out) {
@@ -967,6 +969,30 @@
         }
     }
 
+    /** If display metrics changed and it's not just a rotation - update base values. */
+    private void updateBaseDisplayMetricsIfNeeded() {
+        final int orientation = mDisplayInfo.rotation;
+        final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
+        final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
+        final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight;
+
+        boolean displayMetricsChanged
+                = mBaseDisplayWidth != newWidth || mBaseDisplayHeight != newHeight;
+        displayMetricsChanged |= mBaseDisplayDensity != mDisplayInfo.logicalDensityDpi;
+
+        if (displayMetricsChanged) {
+            updateBaseDisplayMetrics(newWidth, newHeight, mDisplayInfo.logicalDensityDpi);
+            mService.reconfigureDisplayLocked(this);
+        }
+    }
+
+    void updateBaseDisplayMetrics(int baseWidth, int baseHeight, int baseDensity) {
+        mBaseDisplayWidth = mInitialDisplayWidth = baseWidth;
+        mBaseDisplayHeight = mInitialDisplayHeight = baseHeight;
+        mBaseDisplayDensity = mInitialDisplayDensity = baseDensity;
+        mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+    }
+
     void getContentRect(Rect out) {
         out.set(mContentRect);
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 544d1e3..b09d699 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -423,8 +423,12 @@
         mBoundsAfterRotation.setEmpty();
         final DockedStackDividerController controller = getDisplayContent()
                 .mDividerControllerLocked;
-        if (controller.isMinimizedDock() && mStackId == DOCKED_STACK_ID) {
-            outTempBounds.set(controller.getMiddlePositionDockedStackRect());
+        if (mStackId == DOCKED_STACK_ID) {
+            final Rect dockedStackRect = controller.getMiddlePositionDockedStackRect();
+
+            if (dockedStackRect != null) {
+                outTempBounds.set(dockedStackRect);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eae08b3..a53102f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -75,6 +75,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -5931,8 +5932,8 @@
                     if (displayContent.mBaseDisplayWidth != width
                             || displayContent.mBaseDisplayHeight != height) {
                         Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
-                        displayContent.mBaseDisplayWidth = width;
-                        displayContent.mBaseDisplayHeight = height;
+                        displayContent.updateBaseDisplayMetrics(width, height,
+                                displayContent.mBaseDisplayDensity);
                     }
                 } catch (NumberFormatException ex) {
                 }
@@ -5957,8 +5958,7 @@
     // displayContent must not be null
     private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
         Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
-        displayContent.mBaseDisplayWidth = width;
-        displayContent.mBaseDisplayHeight = height;
+        displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity);
         reconfigureDisplayLocked(displayContent);
     }
 
@@ -7914,6 +7914,17 @@
         }
 
         @Override
+        public void updateInputMethodWindowStatus(IBinder imeToken, boolean imeWindowVisible,
+                IBinder targetWindowToken) {
+            // TODO (b/34628091): Use this method to address the window animation issue.
+            if (DEBUG_INPUT_METHOD) {
+                Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
+                        + " imeWindowVisible=" + imeWindowVisible
+                        + " targetWindowToken=" + targetWindowToken);
+            }
+        }
+
+        @Override
         public boolean isHardKeyboardAvailable() {
             synchronized (mWindowMap) {
                 return mHardKeyboardAvailable;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 22dd9d7..2b2bb48 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -99,6 +99,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.Color;
@@ -208,7 +209,7 @@
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
 
-    private static final String LOG_TAG = "DevicePolicyManager";
+    protected static final String LOG_TAG = "DevicePolicyManager";
 
     private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
 
@@ -252,7 +253,6 @@
     private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
             = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
 
-    private static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
     private static final int PROFILE_WIPED_NOTIFICATION_ID = 1001;
     private static final int NETWORK_LOGGING_NOTIFICATION_ID = 1002;
 
@@ -409,6 +409,7 @@
         }
     };
 
+    /** Listens only if mHasFeature == true. */
     private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
 
         @Override
@@ -420,6 +421,7 @@
         }
     };
 
+    /** Listens only if mHasFeature == true. */
     private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
 
         @Override
@@ -513,7 +515,21 @@
 
     final Handler mHandler;
 
-    BroadcastReceiver mReceiver = new BroadcastReceiver() {
+    /** Listens on any device, even when mHasFeature == false. */
+    final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (StorageManager.inCryptKeeperBounce()) {
+                return;
+            }
+            final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+            new MonitoringCertNotificationTask(DevicePolicyManagerService.this, mInjector)
+                    .execute(userHandle);
+        }
+    };
+
+    /** Listens only if mHasFeature == true. */
+    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
@@ -559,14 +575,7 @@
                     }
                 });
             }
-            if (Intent.ACTION_USER_UNLOCKED.equals(action)
-                    || Intent.ACTION_USER_STARTED.equals(action)
-                    || KeyChain.ACTION_TRUST_STORE_CHANGED.equals(action)) {
-                if (!StorageManager.inCryptKeeperBounce()) {
-                    new MonitoringCertNotificationTask().execute(
-                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL));
-                }
-            }
+
             if (Intent.ACTION_USER_ADDED.equals(action)) {
                 sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
                 synchronized (DevicePolicyManagerService.this) {
@@ -1490,6 +1499,15 @@
             mContext = context;
         }
 
+        Context createContextAsUser(UserHandle user) throws PackageManager.NameNotFoundException {
+            final String packageName = mContext.getPackageName();
+            return mContext.createPackageContextAsUser(packageName, 0, user);
+        }
+
+        Resources getResources() {
+            return mContext.getResources();
+        }
+
         Owners newOwners() {
             return new Owners(getUserManager(), getUserManagerInternal(),
                     getPackageManagerInternal());
@@ -1725,6 +1743,10 @@
         boolean securityLogIsLoggingEnabled() {
             return SecurityLog.isLoggingEnabled();
         }
+
+        KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException {
+            return KeyChain.bindAsUser(mContext, user);
+        }
     }
 
     /**
@@ -1755,18 +1777,27 @@
                 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
         mIsWatch = mInjector.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_WATCH);
+
+        // Broadcast filter for changes to the trusted certificate store. These changes get a
+        // separate intent filter so we can listen to them even when device_admin is off.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mContext.registerReceiverAsUser(mRootCaReceiver, UserHandle.ALL, filter, null, mHandler);
+
         if (!mHasFeature) {
             // Skip the rest of the initialization
             return;
         }
-        IntentFilter filter = new IntentFilter();
+
+        filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BOOT_COMPLETED);
         filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
-        filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
@@ -2952,125 +2983,34 @@
         }
     }
 
-    private class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
-        @Override
-        protected Void doInBackground(Integer... params) {
-            int userHandle = params[0];
+    /**
+     * Remove deleted CA certificates from the "approved" list for a particular user, counting
+     * the number still remaining to approve.
+     *
+     * @param userHandle user to check for. This must be a real user and not, for example,
+     *        {@link UserHandle#ALL}.
+     * @param installedCertificates the full set of certificate authorities currently installed for
+     *        {@param userHandle}. After calling this function, {@code mAcceptedCaCertificates} will
+     *        correspond to some subset of this.
+     *
+     * @return number of certificates yet to be approved by {@param userHandle}.
+     */
+    protected synchronized int retainAcceptedCertificates(final UserHandle userHandle,
+            final @NonNull Collection<String> installedCertificates) {
+        enforceManageUsers();
 
-            if (userHandle == UserHandle.USER_ALL) {
-                for (UserInfo userInfo : mUserManager.getUsers(true)) {
-                    manageNotification(userInfo.getUserHandle());
-                }
-            } else {
-                manageNotification(UserHandle.of(userHandle));
-            }
-            return null;
-        }
+        if (!mHasFeature) {
+            return installedCertificates.size();
+        } else {
+            final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
 
-        private void manageNotification(UserHandle userHandle) {
-            if (!mUserManager.isUserUnlocked(userHandle)) {
-                return;
+            // Remove deleted certificates. Flush xml if necessary.
+            if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) {
+                saveSettingsLocked(userHandle.getIdentifier());
             }
 
-            // Call out to KeyChain to check for CAs which are waiting for approval.
-            final List<String> pendingCertificates;
-            try {
-                pendingCertificates = getInstalledCaCertificates(userHandle);
-            } catch (RemoteException | RuntimeException e) {
-                Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
-                return;
-            }
-
-            synchronized (DevicePolicyManagerService.this) {
-                final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
-
-                // Remove deleted certificates. Flush xml if necessary.
-                if (policy.mAcceptedCaCertificates.retainAll(pendingCertificates)) {
-                    saveSettingsLocked(userHandle.getIdentifier());
-                }
-                // Trim to approved certificates.
-                pendingCertificates.removeAll(policy.mAcceptedCaCertificates);
-            }
-
-            if (pendingCertificates.isEmpty()) {
-                mInjector.getNotificationManager().cancelAsUser(
-                        null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
-                return;
-            }
-
-            // Build and show a warning notification
-            int smallIconId;
-            String contentText;
-            int parentUserId = userHandle.getIdentifier();
-            if (getProfileOwner(userHandle.getIdentifier()) != null) {
-                contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed,
-                        getProfileOwnerName(userHandle.getIdentifier()));
-                smallIconId = R.drawable.stat_sys_certificate_info;
-                parentUserId = getProfileParentId(userHandle.getIdentifier());
-            } else if (getDeviceOwnerUserId() == userHandle.getIdentifier()) {
-                contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed,
-                        getDeviceOwnerName());
-                smallIconId = R.drawable.stat_sys_certificate_info;
-            } else {
-                contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_unknown);
-                smallIconId = android.R.drawable.stat_sys_warning;
-            }
-
-            final int numberOfCertificates = pendingCertificates.size();
-            Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
-            dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            dialogIntent.setPackage("com.android.settings");
-            dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, numberOfCertificates);
-            dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
-            PendingIntent notifyIntent = PendingIntent.getActivityAsUser(mContext, 0,
-                    dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
-                    new UserHandle(parentUserId));
-
-            final Context userContext;
-            try {
-                final String packageName = mContext.getPackageName();
-                userContext = mContext.createPackageContextAsUser(packageName, 0, userHandle);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
-                return;
-            }
-            final Notification noti = new Notification.Builder(userContext)
-                .setSmallIcon(smallIconId)
-                .setContentTitle(mContext.getResources().getQuantityText(
-                        R.plurals.ssl_ca_cert_warning, numberOfCertificates))
-                .setContentText(contentText)
-                .setContentIntent(notifyIntent)
-                .setPriority(Notification.PRIORITY_HIGH)
-                .setShowWhen(false)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .build();
-
-            mInjector.getNotificationManager().notifyAsUser(
-                    null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
-        }
-
-        private List<String> getInstalledCaCertificates(UserHandle userHandle)
-                throws RemoteException, RuntimeException {
-            KeyChainConnection conn = null;
-            try {
-                conn = KeyChain.bindAsUser(mContext, userHandle);
-                List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList();
-                List<String> result = new ArrayList<>(aliases.size());
-                for (int i = 0; i < aliases.size(); i++) {
-                    result.add(aliases.get(i).string);
-                }
-                return result;
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                return null;
-            } catch (AssertionError e) {
-                throw new RuntimeException(e);
-            } finally {
-                if (conn != null) {
-                    conn.close();
-                }
-            }
+            // Trim approved certificates from the count.
+            return installedCertificates.size() - policy.mAcceptedCaCertificates.size();
         }
     }
 
@@ -4635,7 +4575,7 @@
             }
             saveSettingsLocked(userId);
         }
-        new MonitoringCertNotificationTask().execute(userId);
+        new MonitoringCertNotificationTask(this, mInjector).execute(userId);
         return true;
     }
 
@@ -4659,7 +4599,7 @@
                     saveSettingsLocked(userInfo.id);
                 }
 
-                new MonitoringCertNotificationTask().execute(userInfo.id);
+                new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id);
             }
         }
     }
@@ -7154,7 +7094,7 @@
         return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
     }
 
-    private int getProfileParentId(int userHandle) {
+    protected int getProfileParentId(int userHandle) {
         final long ident = mInjector.binderClearCallingIdentity();
         try {
             UserInfo parentUser = mUserManager.getProfileParent(userHandle);
@@ -7271,6 +7211,7 @@
             long id = mInjector.binderClearCallingIdentity();
             try {
                 mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle);
+                mIPackageManager.flushPackageRestrictionsAsUser(userHandle);
             } catch (RemoteException re) {
                 // Shouldn't happen
             } finally {
@@ -7289,6 +7230,7 @@
             long id = mInjector.binderClearCallingIdentity();
             try {
                 mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle);
+                mIPackageManager.flushPackageRestrictionsAsUser(userHandle);
             } catch (RemoteException re) {
                 // Shouldn't happen
             } finally {
@@ -10102,7 +10044,9 @@
 
     @Override
     public boolean isDeviceProvisioned() {
-        return !TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT));
+        synchronized (this) {
+            return getUserDataUnchecked(UserHandle.USER_SYSTEM).mUserSetupComplete;
+        }
     }
 
     private void removePackageIfRequired(final String packageName, final int userId) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java b/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java
new file mode 100644
index 0000000..03c137a
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.security.KeyChain.KeyChainConnection;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.util.ParcelableString;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
+    protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
+    protected static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
+
+    private final DevicePolicyManagerService mService;
+    private final DevicePolicyManagerService.Injector mInjector;
+
+    public MonitoringCertNotificationTask(final DevicePolicyManagerService service,
+            final DevicePolicyManagerService.Injector injector) {
+        super();
+        mService = service;
+        mInjector = injector;
+    }
+
+    @Override
+    protected Void doInBackground(Integer... params) {
+        int userHandle = params[0];
+
+        if (userHandle == UserHandle.USER_ALL) {
+            for (UserInfo userInfo : mInjector.getUserManager().getUsers(true)) {
+                repostOrClearNotification(userInfo.getUserHandle());
+            }
+        } else {
+            repostOrClearNotification(UserHandle.of(userHandle));
+        }
+        return null;
+    }
+
+    private void repostOrClearNotification(UserHandle userHandle) {
+        if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+            return;
+        }
+
+        // Call out to KeyChain to check for CAs which are waiting for approval.
+        final int pendingCertificateCount;
+        try {
+            pendingCertificateCount = mService.retainAcceptedCertificates(
+                    userHandle, getInstalledCaCertificates(userHandle));
+        } catch (RemoteException | RuntimeException e) {
+            Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+            return;
+        }
+
+        if (pendingCertificateCount != 0) {
+            showNotification(userHandle, pendingCertificateCount);
+        } else {
+            mInjector.getNotificationManager().cancelAsUser(
+                    LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle);
+        }
+    }
+
+    private void showNotification(UserHandle userHandle, int pendingCertificateCount) {
+        // Create a context for the target user.
+        final Context userContext;
+        try {
+            userContext = mInjector.createContextAsUser(userHandle);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+            return;
+        }
+
+        // Build and show a warning notification
+        int smallIconId;
+        String contentText;
+        int parentUserId = userHandle.getIdentifier();
+        Resources resources = mInjector.getResources();
+        if (mService.getProfileOwner(userHandle.getIdentifier()) != null) {
+            contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
+                    mService.getProfileOwnerName(userHandle.getIdentifier()));
+            smallIconId = R.drawable.stat_sys_certificate_info;
+            parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
+        } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
+            contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
+                    mService.getDeviceOwnerName());
+            smallIconId = R.drawable.stat_sys_certificate_info;
+        } else {
+            contentText = resources.getString(R.string.ssl_ca_cert_noti_by_unknown);
+            smallIconId = android.R.drawable.stat_sys_warning;
+        }
+
+        Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
+        dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        // TODO this next line is taken from original notification code in
+        // {@link DevicePolicyManagerService} but not a very good way of doing it. Do it better.
+        dialogIntent.setPackage("com.android.settings");
+        dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount);
+        dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
+        PendingIntent notifyIntent = PendingIntent.getActivityAsUser(userContext, 0,
+                dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
+                UserHandle.of(parentUserId));
+
+        final Notification noti = new Notification.Builder(userContext)
+            .setSmallIcon(smallIconId)
+            .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
+                    pendingCertificateCount))
+            .setContentText(contentText)
+            .setContentIntent(notifyIntent)
+            .setPriority(Notification.PRIORITY_HIGH)
+            .setShowWhen(false)
+            .setColor(R.color.system_notification_accent_color)
+            .build();
+
+        mInjector.getNotificationManager().notifyAsUser(
+                LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
+    }
+
+    private List<String> getInstalledCaCertificates(UserHandle userHandle)
+            throws RemoteException, RuntimeException {
+        try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) {
+            List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList();
+            List<String> result = new ArrayList<>(aliases.size());
+            for (int i = 0; i < aliases.size(); i++) {
+                result.add(aliases.get(i).string);
+            }
+            return result;
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return null;
+        } catch (AssertionError e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 3b92a34..e6dd13f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -29,6 +29,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.security.KeyChain;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Pair;
@@ -375,5 +376,10 @@
         boolean isBuildDebuggable() {
             return context.buildMock.isDebuggable;
         }
+
+        @Override
+        KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) {
+            return context.keyChainConnection;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 6fb65d5..a186b59 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -21,6 +21,8 @@
 
 import android.Manifest.permission;
 import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -32,6 +34,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.net.IIpConnectivityMetrics;
@@ -52,10 +55,13 @@
 import android.util.Pair;
 
 import com.android.internal.R;
+import com.android.internal.util.ParcelableString;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.UserRestrictionsUtils;
 
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -76,13 +82,16 @@
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -1193,6 +1202,53 @@
         return uid;
     }
 
+    public void testCertificateDisclosure() throws Exception {
+        final int userId = DpmMockContext.CALLER_USER_HANDLE;
+        final UserHandle user = UserHandle.of(userId);
+
+        mContext.applicationInfo = new ApplicationInfo();
+        mContext.callerPermissions.add(permission.MANAGE_USERS);
+        mContext.packageName = "com.android.frameworks.servicestests";
+        mContext.userContexts.put(user, mContext);
+        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        ParceledListSlice<ParcelableString> oneCert = asSlice(new String[] {"1"});
+        ParceledListSlice<ParcelableString> fourCerts = asSlice(new String[] {"1", "2", "3", "4"});
+
+        final String TEST_STRING = "Test for exactly 2 certs out of 4";
+        doReturn(TEST_STRING).when(mContext.resources).getQuantityText(anyInt(), eq(2));
+
+        // Given that we have exactly one certificate installed,
+        when(mContext.keyChainConnection.getService().getUserCaAliases()).thenReturn(oneCert);
+        // when that certificate is approved,
+        dpms.approveCaCert(oneCert.getList().get(0).string, userId, true);
+        // a notification should not be shown.
+        verify(mContext.notificationManager, timeout(1000))
+                .cancelAsUser(anyString(), anyInt(), eq(user));
+
+        // Given that we have four certificates installed,
+        when(mContext.keyChainConnection.getService().getUserCaAliases()).thenReturn(fourCerts);
+        // when two of them are approved (one of them approved twice hence no action),
+        dpms.approveCaCert(fourCerts.getList().get(0).string, userId, true);
+        dpms.approveCaCert(fourCerts.getList().get(1).string, userId, true);
+        // a notification should be shown saying that there are two certificates left to approve.
+        verify(mContext.notificationManager, timeout(1000))
+                .notifyAsUser(anyString(), anyInt(), argThat(
+                        new BaseMatcher<Notification>() {
+                            @Override
+                            public boolean matches(Object item) {
+                                final Notification noti = (Notification) item;
+                                return TEST_STRING.equals(
+                                        noti.extras.getString(Notification.EXTRA_TITLE));
+                            }
+                            @Override
+                            public void describeTo(Description description) {
+                                description.appendText(
+                                        "Notification{title=\"" + TEST_STRING + "\"}");
+                            }
+                        }), eq(user));
+    }
+
     /**
      * Simple test for delegate set/get and general delegation. Tests verifying that delegated
      * privileges can acually be exercised by a delegate are not covered here.
@@ -3734,4 +3790,20 @@
         assertTrue(dpm.setProfileOwner(admin, null, userId));
         mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
     }
+
+    /**
+     * Convert String[] to ParceledListSlice&lt;ParcelableString&gt;.
+     * <p>
+     * TODO: This shouldn't be necessary. If ParcelableString does need to exist, it also needs
+     * a real constructor.
+     */
+    private static ParceledListSlice<ParcelableString> asSlice(String[] s) {
+        List<ParcelableString> list = new ArrayList<>(s.length);
+        for (int i = 0; i < s.length; i++) {
+            ParcelableString item = new ParcelableString();
+            item.string = s[i];
+            list.add(i, item);
+        }
+        return new ParceledListSlice<ParcelableString>(list);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 22cd135..46aaf83 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -43,9 +43,11 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.security.KeyChain;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
+import android.util.ArrayMap;
 import android.view.IWindowManager;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -58,10 +60,12 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -290,6 +294,7 @@
     public final TelephonyManager telephonyManager;
     public final AccountManager accountManager;
     public final AlarmManager alarmManager;
+    public final KeyChain.KeyChainConnection keyChainConnection;
 
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
@@ -300,6 +305,9 @@
 
     public final BuildMock buildMock = new BuildMock();
 
+    /** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */
+    public final Map<UserHandle, Context> userContexts = new ArrayMap<>();
+
     public String packageName = null;
 
     public ApplicationInfo applicationInfo = null;
@@ -335,6 +343,7 @@
         telephonyManager = mock(TelephonyManager.class);
         accountManager = mock(AccountManager.class);
         alarmManager = mock(AlarmManager.class);
+        keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(context.getPackageManager());
@@ -690,6 +699,19 @@
     }
 
     @Override
+    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+            throws PackageManager.NameNotFoundException {
+        if (!userContexts.containsKey(user)) {
+            return super.createPackageContextAsUser(packageName, flags, user);
+        }
+        if (!getPackageName().equals(packageName)) {
+            throw new UnsupportedOperationException(
+                    "Creating a context as another package is not implemented");
+        }
+        return userContexts.get(user);
+    }
+
+    @Override
     public ContentResolver getContentResolver() {
         return contentResolver;
     }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 32f487b..dd03305 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -20,17 +20,9 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.telephony.SubscriptionManager;
-import android.telephony.CellLocation;
-import android.telephony.CellInfo;
-import android.telephony.VoLteServiceState;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.PreciseCallState;
-import android.telephony.PreciseDataConnectionState;
 
 import com.android.internal.telephony.IPhoneStateListener;
+
 import java.util.List;
 import java.lang.ref.WeakReference;
 
@@ -228,6 +220,38 @@
      */
     public static final int LISTEN_CARRIER_NETWORK_CHANGE                   = 0x00010000;
 
+    /**
+     *  Listen for changes to the sim voice activation state
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     *  {@more}
+     *  Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+     *  fully activated
+     *
+     *  @see #onVoiceActivationStateChanged
+     *  @hide
+     */
+    public static final int LISTEN_VOICE_ACTIVATION_STATE                   = 0x00020000;
+
+    /**
+     *  Listen for changes to the sim data activation state
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     *  @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     *  {@more}
+     *  Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+     *  fully activated
+     *
+     *  @see #onDataActivationStateChanged
+     *  @hide
+     */
+    public static final int LISTEN_DATA_ACTIVATION_STATE                   = 0x00040000;
+
      /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -327,6 +351,12 @@
                     case LISTEN_VOLTE_STATE:
                         PhoneStateListener.this.onVoLteServiceStateChanged((VoLteServiceState)msg.obj);
                         break;
+                    case LISTEN_VOICE_ACTIVATION_STATE:
+                        PhoneStateListener.this.onVoiceActivationStateChanged((int)msg.obj);
+                        break;
+                    case LISTEN_DATA_ACTIVATION_STATE:
+                        PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
+                        break;
                     case LISTEN_OEM_HOOK_RAW_EVENT:
                         PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
                         break;
@@ -506,6 +536,24 @@
     }
 
     /**
+     * Callback invoked when the SIM voice activation state has changed
+     * @param state is the current SIM voice activation state
+     * @hide
+     */
+    public void onVoiceActivationStateChanged(int state) {
+
+    }
+
+    /**
+     * Callback invoked when the SIM data activation state has changed
+     * @param state is the current SIM data activation state
+     * @hide
+     */
+    public void onDataActivationStateChanged(int state) {
+
+    }
+
+    /**
      * Callback invoked when OEM hook raw event is received. Requires
      * the READ_PRIVILEGED_PHONE_STATE permission.
      * @param rawData is the byte array of the OEM hook raw data.
@@ -619,6 +667,14 @@
             send(LISTEN_VOLTE_STATE, 0, 0, lteState);
         }
 
+        public void onVoiceActivationStateChanged(int activationState) {
+            send(LISTEN_VOICE_ACTIVATION_STATE, 0, 0, activationState);
+        }
+
+        public void onDataActivationStateChanged(int activationState) {
+            send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
+        }
+
         public void onOemHookRawEvent(byte[] rawData) {
             send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1b3aa8a..4ff4d80 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2813,6 +2813,148 @@
     }
 
     /**
+     * Initial SIM activation state, unknown. Not set by any carrier apps.
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0;
+
+    /**
+     * indicate SIM is under activation procedure now.
+     * intermediate state followed by another state update with activation procedure result:
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1;
+
+    /**
+     * Indicate SIM has been successfully activated with full service
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2;
+
+    /**
+     * Indicate SIM has been deactivated by the carrier so that service is not available
+     * and requires activation service to enable services.
+     * Carrier apps could be signalled to set activation state to deactivated if detected
+     * deactivated sim state and set it back to activated after successfully run activation service.
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3;
+
+    /**
+     * Restricted state indicate SIM has been activated but service are restricted.
+     * note this is currently available for data activation state. For example out of byte sim.
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4;
+
+    /**
+     * Sets the voice activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @param subId The subscription id.
+     * @param activationState The voice activation state of the given subscriber.
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @hide
+     */
+    public void setVoiceActivationState(int subId, int activationState) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setVoiceActivationState(subId, activationState);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
+     * Sets the data activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @param subId The subscription id.
+     * @param activationState The data activation state of the given subscriber.
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    public void setDataActivationState(int subId, int activationState) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setDataActivationState(subId, activationState);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
+     * Returns the voice activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
+     * @param subId The subscription id.
+     *
+     * @return voiceActivationState for the given subscriber
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @hide
+     */
+    public int getVoiceActivationState(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.getVoiceActivationState(subId, getOpPackageName());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return SIM_ACTIVATION_STATE_UNKNOWN;
+    }
+
+    /**
+     * Returns the data activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
+     * @param subId The subscription id.
+     *
+     * @return dataActivationState for the given subscriber
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    public int getDataActivationState(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.getDataActivationState(subId, getOpPackageName());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return SIM_ACTIVATION_STATE_UNKNOWN;
+    }
+
+    /**
      * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages
      * but the count is unknown.
      * <p>
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index cbedb95..e9c5461 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -43,6 +43,8 @@
     void onPreciseDataConnectionStateChanged(in PreciseDataConnectionState dataConnectionState);
     void onDataConnectionRealTimeInfoChanged(in DataConnectionRealTimeInfo dcRtInfo);
     void onVoLteServiceStateChanged(in VoLteServiceState lteState);
+    void onVoiceActivationStateChanged(int activationState);
+    void onDataActivationStateChanged(int activationState);
     void onOemHookRawEvent(in byte[] rawData);
     void onCarrierNetworkChange(in boolean active);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f2b8804..d90a33e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -456,6 +456,30 @@
      */
     boolean setVoiceMailNumber(int subId, String alphaTag, String number);
 
+     /**
+      * Sets the voice activation state for a particular subscriber.
+      */
+    void setVoiceActivationState(int subId, int activationState);
+
+     /**
+      * Sets the data activation state for a particular subscriber.
+      */
+    void setDataActivationState(int subId, int activationState);
+
+     /**
+      * Returns the voice activation state for a particular subscriber.
+      * @param subId user preferred sub
+      * @param callingPackage package queries voice activation state
+      */
+    int getVoiceActivationState(int subId, String callingPackage);
+
+     /**
+      * Returns the data activation state for a particular subscriber.
+      * @param subId user preferred sub
+      * @param callingPackage package queris data activation state
+      */
+    int getDataActivationState(int subId, String callingPackage);
+
     /**
       * Returns the unread count of voicemails
       */
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c6be62..2c2206c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -65,6 +65,8 @@
             String failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
     void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
+    void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
+            int activationState, int activationType);
     void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
     void notifySubscriptionInfoChanged();
     void notifyCarrierNetworkChange(in boolean active);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index fdc68b9..f9de776 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -74,6 +74,9 @@
     public static final int PRESENTATION_UNKNOWN = 3;    // no specified or unknown by network
     public static final int PRESENTATION_PAYPHONE = 4;   // show pay phone info
 
+    // Sim activation type
+    public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
+    public static final int SIM_ACTIVATION_TYPE_DATA = 1;
 
     public static final String PHONE_NAME_KEY = "phoneName";
     public static final String FAILURE_REASON_KEY = "reason";
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 0dcd0f1..d51d75e 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -24,6 +24,7 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ChangedPackages;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
@@ -374,6 +375,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public ChangedPackages getChangedPackages(int sequenceNumber) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
         throw new UnsupportedOperationException();
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 0aa20ef..8a5fd4b 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,43 +15,43 @@
  */
 package com.android.tests.applaunch;
 
-import java.io.OutputStreamWriter;
-
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
+import android.app.IActivityManager;
+import android.app.UiAutomation;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.app.UiAutomation;
-import android.app.IActivityManager;
 import android.support.test.rule.logging.AtraceLogger;
 import android.test.InstrumentationTestCase;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.ArrayList;
 import java.util.Map;
 import java.util.Set;
-import android.os.ParcelFileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.InputStreamReader;
-
 /**
  * This test is intended to measure the time it takes for the apps to start.
  * Names of the applications are passed in command line, and the
@@ -73,6 +73,7 @@
     private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
     private static final String KEY_LAUNCH_ORDER = "launch_order";
     private static final String KEY_DROP_CACHE = "drop_cache";
+    private static final String KEY_SIMULATE_MAINTANANCE = "simulate_maintanance";
     private static final String KEY_SIMPLEPPERF_CMD = "simpleperf_cmd";
     private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
     private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
@@ -97,6 +98,7 @@
     private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
     private static final String APP_LAUNCH_CMD = "am start -W -n";
     private static final String SUCCESS_MESSAGE = "Status: ok";
+    private static final String PROFILE_COMPILE_SUCCESS = "Success";
     private static final String THIS_TIME = "ThisTime:";
     private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
     private static final String TRACE_ITERATION = "TRACE_ITERATION - %d";
@@ -104,6 +106,8 @@
     private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
     private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
     private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
+    private static final String SPEED_PROFILE_CMD = "cmd package compile -f -m speed-profile %s";
+
 
 
     private Map<String, Intent> mNameToIntent;
@@ -124,6 +128,7 @@
     private File mFile = null;
     private FileOutputStream mOutputStream = null;
     private BufferedWriter mBufferedWriter = null;
+    private boolean mSimulateMaintanance = false;
 
 
     @Override
@@ -149,6 +154,8 @@
         mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
         mSimplePerfCmd = args.getString(KEY_SIMPLEPPERF_CMD);
         mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
+        mSimulateMaintanance =  Boolean.parseBoolean(args.getString(KEY_SIMULATE_MAINTANANCE));
+
         createMappings();
         parseArgs(args);
         checkAccountSignIn();
@@ -229,6 +236,12 @@
                     sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
                     closeApp(launch.getApp(), true);
                     dropCache();
+                    if (mSimulateMaintanance) {
+                        String appPkgName = mNameToIntent.get(launch.getApp())
+                                .getComponent().getPackageName();
+                        assertTrue(String.format("Not able to speed profile the app : %s",
+                                appPkgName), profileCompileApp(appPkgName));
+                    }
                     sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
                 }
 
@@ -305,6 +318,26 @@
     }
 
     /**
+     * Compile the app package using speed compile command and return true or false
+     * based on status of the compilation command.
+     */
+    private boolean profileCompileApp(String appPkgName) throws IOException {
+        Log.i(TAG, "Starting to speed profile " + appPkgName);
+        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                executeShellCommand(String.format(SPEED_PROFILE_CMD, appPkgName));
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                if (line.contains(PROFILE_COMPILE_SUCCESS)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
      * If launch order is "cyclic" then apps will be launched one after the
      * other for each iteration count.
      * If launch order is "sequential" then each app will be launched for given number
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index 0fa4545..32e1b96 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -210,10 +210,9 @@
         inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
-        // TODO: Verify proper cleanup is performed:
-        // inOrder.verify(mStatsService).forceUpdate();
-        // inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        // inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mStatsService).forceUpdate();
+        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
     }
 
     @Test
@@ -230,10 +229,9 @@
         inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        // TODO: Verify proper cleanup is performed:
-        // inOrder.verify(mStatsService).forceUpdate();
-        // inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        // inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mStatsService).forceUpdate();
+        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
+        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
     }
 
     @Test
@@ -296,6 +294,18 @@
                 IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
     }
 
+    @Test
+    public void ignoresDuplicateUpstreamNotifications() throws Exception {
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+
+        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+
+        for (int i = 0; i < 5; i++) {
+            dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+            verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        }
+    }
+
     /**
      * Send a command to the state machine under test, and run the event loop to idle.
      *
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index eee7473..174bbcf 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -863,29 +863,6 @@
             }
 
             mLocalMatrix.set(copy.mLocalMatrix);
-
-            final ArrayList<Object> children = copy.mChildren;
-            //noinspection ForLoopReplaceableByForEach
-            for (int i = 0; i < children.size(); i++) {
-                Object copyChild = children.get(i);
-                if (copyChild instanceof VGroup_Delegate) {
-                    VGroup_Delegate copyGroup = (VGroup_Delegate) copyChild;
-                    mChildren.add(new VGroup_Delegate(copyGroup, targetsMap));
-                } else {
-                    VPath_Delegate newPath;
-                    if (copyChild instanceof VFullPath_Delegate) {
-                        newPath = new VFullPath_Delegate((VFullPath_Delegate) copyChild);
-                    } else if (copyChild instanceof VClipPath_Delegate) {
-                        newPath = new VClipPath_Delegate((VClipPath_Delegate) copyChild);
-                    } else {
-                        throw new IllegalStateException("Unknown object in the tree!");
-                    }
-                    mChildren.add(newPath);
-                    if (newPath.mPathName != null) {
-                        targetsMap.put(newPath.mPathName, newPath);
-                    }
-                }
-            }
         }
 
         private VGroup_Delegate() {
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index b19cb58..b6e6ec0 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -38,7 +38,10 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.widget.ImageView;
 import android.widget.NumberPicker;
 
 import java.io.File;
@@ -401,6 +404,15 @@
                     numberPicker.setMaxValue(Integer.parseInt(maxValue));
                 }
             }
+            else if (view instanceof ImageView) {
+                ImageView img = (ImageView) view;
+                Drawable drawable = img.getDrawable();
+                if (drawable instanceof Animatable) {
+                    if (!((Animatable) drawable).isRunning()) {
+                        ((Animatable) drawable).start();
+                    }
+                }
+            }
 
         }
     }
diff --git a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
index 381eb1f..494ffa1 100644
--- a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
@@ -18,6 +18,7 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.java.System_Delegate;
 
 import java.lang.reflect.Field;
 import java.util.concurrent.atomic.AtomicReference;
@@ -54,8 +55,8 @@
     public static void doFrame(long frameTimeNanos) {
         Choreographer thisChoreographer = Choreographer.getInstance();
 
-        thisChoreographer.mLastFrameTimeNanos = frameTimeNanos;
-
+        thisChoreographer.mLastFrameTimeNanos = frameTimeNanos - thisChoreographer
+                .getFrameIntervalNanos();
         thisChoreographer.mFrameInfo.markInputHandlingStart();
         thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 77b131f..4ffb2e2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -132,7 +132,7 @@
     @Override
     public Result insertChild(Object parentView, ILayoutPullParser childXml, int index,
             IAnimationListener listener) {
-        if (parentView instanceof ViewGroup == false) {
+        if (!(parentView instanceof ViewGroup)) {
             throw new IllegalArgumentException("parentView is not a ViewGroup");
         }
 
@@ -155,10 +155,10 @@
     @Override
     public Result moveChild(Object parentView, Object childView, int index,
             Map<String, String> layoutParams, IAnimationListener listener) {
-        if (parentView instanceof ViewGroup == false) {
+        if (!(parentView instanceof ViewGroup)) {
             throw new IllegalArgumentException("parentView is not a ViewGroup");
         }
-        if (childView instanceof View == false) {
+        if (!(childView instanceof View)) {
             throw new IllegalArgumentException("childView is not a View");
         }
 
@@ -179,7 +179,7 @@
 
     @Override
     public Result removeChild(Object childView, IAnimationListener listener) {
-        if (childView instanceof View == false) {
+        if (!(childView instanceof View)) {
             throw new IllegalArgumentException("childView is not a View");
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index d392f21..91668af 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -100,7 +100,7 @@
         mView.setText(text);
     }
 
-    public void setGravity(int gravity) {
+    private void setGravity(int gravity) {
         mView.setGravity(gravity);
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 00799a1..0039476 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -24,6 +24,7 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ChangedPackages;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
@@ -859,6 +860,11 @@
     }
 
     @Override
+    public ChangedPackages getChangedPackages(int sequenceNumber) {
+        return null;
+    }
+
+    @Override
     public boolean isUpgrade() {
         return false;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index 2fe3ed5..287334c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -305,6 +305,7 @@
         return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public void requestFitSystemWindows() {
         // The framework call would usually bubble up to ViewRootImpl but, in layoutlib, Layout will
@@ -416,6 +417,7 @@
             }
         }
 
+        @SuppressWarnings("SameParameterValue")
         private int getDimension(String attr, boolean isFramework, int defaultValue) {
             ResourceValue value = mResources.findItemInTheme(attr, isFramework);
             value = mResources.resolveResValue(value);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 85fe2a4..d21955e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -144,7 +144,7 @@
     private static final class PostInflateException extends Exception {
         private static final long serialVersionUID = 1L;
 
-        public PostInflateException(String message) {
+        private PostInflateException(String message) {
             super(message);
         }
     }
@@ -242,11 +242,13 @@
             // Then measure only the content with UNSPECIFIED to see the size difference
             // and apply this to the screen size.
 
+            View measuredView = mContentRoot.getChildAt(0);
+
             // first measure the full layout, with EXACTLY to get the size of the
             // content as it is inside the decor/dialog
             @SuppressWarnings("deprecation")
             Pair<Integer, Integer> exactMeasure = measureView(
-                    mViewRoot, mContentRoot.getChildAt(0),
+                    mViewRoot, measuredView,
                     mMeasuredScreenWidth, MeasureSpec.EXACTLY,
                     mMeasuredScreenHeight, MeasureSpec.EXACTLY);
 
@@ -258,6 +260,10 @@
                     mMeasuredScreenWidth, widthMeasureSpecMode,
                     mMeasuredScreenHeight, heightMeasureSpecMode);
 
+            // If measuredView is not null, exactMeasure nor result will be null.
+            assert exactMeasure != null;
+            assert result != null;
+
             // now look at the difference and add what is needed.
             if (renderingMode.isHorizExpand()) {
                 int measuredWidth = exactMeasure.getFirst();
@@ -406,8 +412,7 @@
      * @param canvas an optional canvas to render the views to. If null, only the measure and
      * layout steps will be executed.
      */
-    private static Result renderAndBuildResult(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
-            @Nullable Canvas canvas, int width, int height) {
+    private static Result renderAndBuildResult(@NonNull ViewGroup viewRoot, @Nullable Canvas canvas) {
         if (canvas == null) {
             return SUCCESS.createResult();
         }
@@ -551,7 +556,7 @@
                     long initialTime = System_Delegate.nanoTime();
                     if (!mFirstFrameExecuted) {
                         // We need to run an initial draw call to initialize the animations
-                        renderAndBuildResult(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
+                        renderAndBuildResult(mViewRoot, NOP_CANVAS);
 
                         // The first frame will initialize the animations
                         Choreographer_Delegate.doFrame(initialTime);
@@ -560,8 +565,7 @@
                     // Second frame will move the animations
                     Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
                 }
-                renderResult = renderAndBuildResult(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
-                        mMeasuredScreenHeight);
+                renderResult = renderAndBuildResult(mViewRoot, mCanvas);
             }
 
             mSystemViewInfoList =
@@ -1206,7 +1210,7 @@
      * Sets up a {@link TabHost} object.
      * @param tabHost the TabHost to setup.
      * @param layoutlibCallback The project callback object to access the project R class.
-     * @throws PostInflateException
+     * @throws PostInflateException if TabHost is missing the required ids for TabHost
      */
     private void setupTabHost(TabHost tabHost, LayoutlibCallback layoutlibCallback)
             throws PostInflateException {
@@ -1254,12 +1258,7 @@
             TabSpec spec = tabHost.newTabSpec("tag")
                     .setIndicator("Tab Label", tabHost.getResources()
                             .getDrawable(android.R.drawable.ic_menu_info_details, null))
-                    .setContent(new TabHost.TabContentFactory() {
-                        @Override
-                        public View createTabContent(String tag) {
-                            return new LinearLayout(getContext());
-                        }
-                    });
+                    .setContent(tag -> new LinearLayout(getContext()));
             tabHost.addTab(spec);
         } else {
             // for each child of the frameLayout, add a new TabSpec
@@ -1333,8 +1332,8 @@
 
         int childCount = viewGroup.getChildCount();
         if (viewGroup == mContentRoot) {
-            List<ViewInfo> childrenWithoutOffset = new ArrayList<ViewInfo>(childCount);
-            List<ViewInfo> childrenWithOffset = new ArrayList<ViewInfo>(childCount);
+            List<ViewInfo> childrenWithoutOffset = new ArrayList<>(childCount);
+            List<ViewInfo> childrenWithOffset = new ArrayList<>(childCount);
             for (int i = 0; i < childCount; i++) {
                 ViewInfo[] childViewInfo =
                         visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset,
@@ -1345,7 +1344,7 @@
             mViewInfoList = childrenWithOffset;
             return childrenWithoutOffset;
         } else {
-            List<ViewInfo> children = new ArrayList<ViewInfo>(childCount);
+            List<ViewInfo> children = new ArrayList<>(childCount);
             for (int i = 0; i < childCount; i++) {
                 children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, setExtendedInfo,
                         isContentFrame));
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
index 96b795a..f149b6c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -34,12 +34,9 @@
 
     @Override
     public boolean markSupported() {
-        if (mFakeMarkSupport) {
-            // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
-            return true;
-        }
+        // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
+        return mFakeMarkSupport || super.markSupported();
 
-        return super.markSupported();
     }
 
     public void disableFakeMarkSupport() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
index 040191e..b89718f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -52,9 +52,7 @@
         Exception ex;
         try {
             return method.invoke(object, args);
-        } catch (IllegalAccessException e) {
-            ex = e;
-        } catch (InvocationTargetException e) {
+        } catch (IllegalAccessException | InvocationTargetException e) {
             ex = e;
         }
         throw new ReflectionException(ex);
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
index c7f2c41..7611cde 100644
--- a/tools/layoutlib/create/Android.mk
+++ b/tools/layoutlib/create/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_JAR_MANIFEST := manifest.txt
 LOCAL_STATIC_JAVA_LIBRARIES := \
-	asm-5.0
+	asm-5.2
 
 LOCAL_MODULE := layoutlib_create
 
diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk
index 61e381d..488d7d6 100644
--- a/tools/layoutlib/create/tests/Android.mk
+++ b/tools/layoutlib/create/tests/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_JAVA_LIBRARIES := layoutlib_create junit-host
-LOCAL_STATIC_JAVA_LIBRARIES := asm-5.0
+LOCAL_STATIC_JAVA_LIBRARIES := asm-5.2
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index c53cd3c..7a3cddf 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -49,6 +49,14 @@
     private WifiSsid() {
     }
 
+    public static WifiSsid createFromByteArray(byte ssid[]) {
+        WifiSsid wifiSsid = new WifiSsid();
+        if (ssid != null) {
+            wifiSsid.octets.write(ssid, 0/* the start offset */, ssid.length);;
+        }
+        return wifiSsid;
+    }
+
     public static WifiSsid createFromAsciiEncoded(String asciiEncoded) {
         WifiSsid a = new WifiSsid();
         a.convertToBytes(asciiEncoded);
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
new file mode 100644
index 0000000..c7bdb7b
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.net.wifi;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiSsid}.
+ */
+public class WifiSsidTest {
+
+    private static final byte[] TEST_SSID =
+            new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+    /**
+     * Check that createFromByteArray() works.
+     */
+    @Test
+    public void testCreateFromByteArray() {
+        WifiSsid wifiSsid = WifiSsid.createFromByteArray(TEST_SSID);
+        assertTrue(wifiSsid != null);
+        assertEquals(new String(TEST_SSID), wifiSsid.toString());
+    }
+}