Merge "Camera: Initial support for vendor tag caches" into oc-dev
diff --git a/Android.mk b/Android.mk
index e27aa30..01fb73e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -224,6 +224,7 @@
 	core/java/android/net/IEthernetManager.aidl \
 	core/java/android/net/IEthernetServiceListener.aidl \
 	core/java/android/net/INetdEventCallback.aidl \
+	core/java/android/net/IIpSecService.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/net/INetworkPolicyListener.aidl \
 	core/java/android/net/INetworkPolicyManager.aidl \
@@ -320,8 +321,6 @@
 	core/java/android/service/wallpaper/IWallpaperService.aidl \
 	core/java/android/service/chooser/IChooserTargetService.aidl \
 	core/java/android/service/chooser/IChooserTargetResult.aidl \
-	core/java/android/service/resolver/IResolverRankerService.aidl \
-	core/java/android/service/resolver/IResolverRankerResult.aidl \
 	core/java/android/text/ITextClassificationService.aidl \
 	core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
 	core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
@@ -730,7 +729,6 @@
 	frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \
 	frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
 	frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
-	frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \
 	frameworks/base/core/java/android/speech/tts/Voice.aidl \
 	frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \
 	frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3c90844..c154fc9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1609,6 +1609,7 @@
     field public static final int alert_light_frame = 17301505; // 0x1080001
     field public static final int arrow_down_float = 17301506; // 0x1080002
     field public static final int arrow_up_float = 17301507; // 0x1080003
+    field public static final int autofilled_highlight = 17301684; // 0x10800b4
     field public static final int bottom_bar = 17301658; // 0x108009a
     field public static final int btn_default = 17301508; // 0x1080004
     field public static final int btn_default_small = 17301509; // 0x1080005
@@ -3578,7 +3579,7 @@
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
     method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public void finish();
     method public void finishActivity(int);
     method public void finishActivityFromChild(android.app.Activity, int);
@@ -4358,7 +4359,7 @@
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public boolean dispatchTouchEvent(android.view.MotionEvent);
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public android.app.ActionBar getActionBar();
     method public final android.content.Context getContext();
     method public android.view.View getCurrentFocus();
@@ -4771,7 +4772,7 @@
     method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
     method public abstract int getBackStackEntryCount();
     method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String);
-    method public abstract java.util.Collection<android.app.Fragment> getFragments();
+    method public abstract java.util.List<android.app.Fragment> getFragments();
     method public abstract android.app.Fragment getPrimaryNavigationFragment();
     method public void invalidateOptionsMenu();
     method public abstract boolean isDestroyed();
@@ -7044,6 +7045,7 @@
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
     method public android.os.Bundle getAppWidgetOptions(int);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+    method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
     method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
     method public boolean isRequestPinAppWidgetSupported();
@@ -25556,7 +25558,7 @@
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
     method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
-    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
   }
 
@@ -25590,7 +25592,6 @@
     method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
-    method public android.net.IpSecTransform.Builder setSpi(int, int);
     method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
   }
 
@@ -37051,7 +37052,7 @@
     ctor public FillResponse.Builder();
     method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
     method public android.service.autofill.FillResponse build();
-    method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews);
+    method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
@@ -37067,8 +37068,10 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
     field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3
+    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5
     field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
     field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
+    field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4
   }
 
   public static final class SaveInfo.Builder {
@@ -38750,6 +38753,7 @@
     method public java.lang.String getCallerDisplayName();
     method public int getCallerDisplayNamePresentation();
     method public final long getConnectTimeMillis();
+    method public long getCreationTimeMillis();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public android.os.Bundle getExtras();
     method public android.telecom.GatewayInfo getGatewayInfo();
@@ -46605,7 +46609,7 @@
     method public void clearFlags(int);
     method public abstract void closeAllPanels();
     method public abstract void closePanel(int);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public boolean getAllowEnterTransitionOverlap();
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.view.WindowManager.LayoutParams getAttributes();
diff --git a/api/system-current.txt b/api/system-current.txt
index 6a14b15..6c34d40 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -53,7 +53,6 @@
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
     field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
-    field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
     field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
     field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
@@ -1728,6 +1727,7 @@
     field public static final int alert_light_frame = 17301505; // 0x1080001
     field public static final int arrow_down_float = 17301506; // 0x1080002
     field public static final int arrow_up_float = 17301507; // 0x1080003
+    field public static final int autofilled_highlight = 17301684; // 0x10800b4
     field public static final int bottom_bar = 17301658; // 0x108009a
     field public static final int btn_default = 17301508; // 0x1080004
     field public static final int btn_default_small = 17301509; // 0x1080005
@@ -3703,7 +3703,7 @@
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
     method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public void finish();
     method public void finishActivity(int);
     method public void finishActivityFromChild(android.app.Activity, int);
@@ -4510,7 +4510,7 @@
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public boolean dispatchTouchEvent(android.view.MotionEvent);
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public android.app.ActionBar getActionBar();
     method public final android.content.Context getContext();
     method public android.view.View getCurrentFocus();
@@ -4932,7 +4932,7 @@
     method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
     method public abstract int getBackStackEntryCount();
     method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String);
-    method public abstract java.util.Collection<android.app.Fragment> getFragments();
+    method public abstract java.util.List<android.app.Fragment> getFragments();
     method public abstract android.app.Fragment getPrimaryNavigationFragment();
     method public void invalidateOptionsMenu();
     method public abstract boolean isDestroyed();
@@ -7504,6 +7504,7 @@
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
     method public android.os.Bundle getAppWidgetOptions(int);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+    method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
     method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
     method public boolean isRequestPinAppWidgetSupported();
@@ -27718,13 +27719,11 @@
   public final class IpSecManager {
     method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
     method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
-    method public void applyTunnelModeTransform(android.net.Network, android.net.IpSecTransform);
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
     method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
-    method public void removeTunnelModeTransform(android.net.Network, android.net.IpSecTransform);
-    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
   }
 
@@ -27755,12 +27754,10 @@
   public static class IpSecTransform.Builder {
     ctor public IpSecTransform.Builder(android.content.Context);
     method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
-    method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, java.net.InetAddress);
     method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
     method public android.net.IpSecTransform.Builder setNattKeepalive(int);
-    method public android.net.IpSecTransform.Builder setSpi(int, int);
     method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
     method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
   }
@@ -40117,7 +40114,7 @@
     ctor public FillResponse.Builder();
     method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
     method public android.service.autofill.FillResponse build();
-    method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews);
+    method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
@@ -40133,8 +40130,10 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
     field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3
+    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5
     field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
     field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
+    field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4
   }
 
   public static final class SaveInfo.Builder {
@@ -40620,36 +40619,6 @@
 
 }
 
-package android.service.resolver {
-
-  public abstract class ResolverRankerService extends android.app.Service {
-    ctor public ResolverRankerService();
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public void onPredictSharingProbabilities(java.util.List<android.service.resolver.ResolverTarget>);
-    method public void onTrainRankingModel(java.util.List<android.service.resolver.ResolverTarget>, int);
-    field public static final java.lang.String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService";
-  }
-
-  public final class ResolverTarget implements android.os.Parcelable {
-    ctor public ResolverTarget();
-    method public int describeContents();
-    method public float getChooserScore();
-    method public float getLaunchScore();
-    method public float getRecencyScore();
-    method public float getSelectProbability();
-    method public float getTimeSpentScore();
-    method public void setChooserScore(float);
-    method public void setLaunchScore(float);
-    method public void setRecencyScore(float);
-    method public void setSelectProbability(float);
-    method public void setTimeSpentScore(float);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.resolver.ResolverTarget> CREATOR;
-  }
-
-}
-
 package android.service.restrictions {
 
   public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
@@ -41973,6 +41942,7 @@
     method public java.lang.String getCallerDisplayName();
     method public int getCallerDisplayNamePresentation();
     method public final long getConnectTimeMillis();
+    method public long getCreationTimeMillis();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public android.os.Bundle getExtras();
     method public android.telecom.GatewayInfo getGatewayInfo();
@@ -50129,7 +50099,7 @@
     method public void clearFlags(int);
     method public abstract void closeAllPanels();
     method public abstract void closePanel(int);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public boolean getAllowEnterTransitionOverlap();
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.view.WindowManager.LayoutParams getAttributes();
diff --git a/api/test-current.txt b/api/test-current.txt
index c5dc584..e41fde3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1609,6 +1609,7 @@
     field public static final int alert_light_frame = 17301505; // 0x1080001
     field public static final int arrow_down_float = 17301506; // 0x1080002
     field public static final int arrow_up_float = 17301507; // 0x1080003
+    field public static final int autofilled_highlight = 17301684; // 0x10800b4
     field public static final int bottom_bar = 17301658; // 0x108009a
     field public static final int btn_default = 17301508; // 0x1080004
     field public static final int btn_default_small = 17301509; // 0x1080005
@@ -3580,7 +3581,7 @@
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
     method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public void finish();
     method public void finishActivity(int);
     method public void finishActivityFromChild(android.app.Activity, int);
@@ -4370,7 +4371,7 @@
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public boolean dispatchTouchEvent(android.view.MotionEvent);
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public android.app.ActionBar getActionBar();
     method public final android.content.Context getContext();
     method public android.view.View getCurrentFocus();
@@ -4783,7 +4784,7 @@
     method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
     method public abstract int getBackStackEntryCount();
     method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String);
-    method public abstract java.util.Collection<android.app.Fragment> getFragments();
+    method public abstract java.util.List<android.app.Fragment> getFragments();
     method public abstract android.app.Fragment getPrimaryNavigationFragment();
     method public void invalidateOptionsMenu();
     method public abstract boolean isDestroyed();
@@ -7074,6 +7075,7 @@
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
     method public android.os.Bundle getAppWidgetOptions(int);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+    method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForPackage(java.lang.String, android.os.UserHandle);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfile(android.os.UserHandle);
     method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
     method public boolean isRequestPinAppWidgetSupported();
@@ -25669,7 +25671,7 @@
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
     method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
-    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
   }
 
@@ -25703,7 +25705,6 @@
     method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
     method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
-    method public android.net.IpSecTransform.Builder setSpi(int, int);
     method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
   }
 
@@ -37209,7 +37210,7 @@
     ctor public FillResponse.Builder();
     method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
     method public android.service.autofill.FillResponse build();
-    method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender, android.widget.RemoteViews);
+    method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
@@ -37225,8 +37226,10 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
     field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3
+    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5
     field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
     field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
+    field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4
   }
 
   public static final class SaveInfo.Builder {
@@ -38954,6 +38957,7 @@
     method public java.lang.String getCallerDisplayName();
     method public int getCallerDisplayNamePresentation();
     method public final long getConnectTimeMillis();
+    method public long getCreationTimeMillis();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public android.os.Bundle getExtras();
     method public android.telecom.GatewayInfo getGatewayInfo();
@@ -46036,6 +46040,7 @@
     method public void setAnimation(android.view.animation.Animation);
     method public void setAutofillHints(java.lang.String...);
     method public void setAutofillMode(int);
+    method public void setAutofilled(boolean);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
     method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -46987,7 +46992,7 @@
     method public void clearFlags(int);
     method public abstract void closeAllPanels();
     method public abstract void closePanel(int);
-    method public android.view.View findViewById(int);
+    method public <T extends android.view.View> T findViewById(int);
     method public boolean getAllowEnterTransitionOverlap();
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.view.WindowManager.LayoutParams getAttributes();
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 35011b5..8442585 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -40,8 +40,8 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -98,11 +98,10 @@
             "alwaysPromptForAccount";
 
     /**
-     * If set then this string willb e used as the description rather than
+     * If set then this string will be used as the description rather than
      * the default.
      */
-    public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE =
-            "descriptionTextOverride";
+    public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE = "descriptionTextOverride";
 
     public static final int REQUEST_NULL = 0;
     public static final int REQUEST_CHOOSE_TYPE = 1;
@@ -112,7 +111,8 @@
     private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts";
     private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName";
     private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount";
-    private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountAndVisibilityList";
+    private static final String KEY_INSTANCE_STATE_ACCOUNTS_LIST = "accountsList";
+    private static final String KEY_INSTANCE_STATE_VISIBILITY_LIST = "visibilityList";
 
     private static final int SELECTED_ITEM_NONE = -1;
 
@@ -122,7 +122,7 @@
     private boolean mSelectedAddNewAccount = false;
     private String mDescriptionOverride;
 
-    private Map<Account, Integer> mAccounts;
+    private LinkedHashMap<Account, Integer> mAccounts;
     // TODO Redesign flow to show NOT_VISIBLE accounts
     // and display a warning if they are selected.
     // Currently NOT_VISBILE accounts are not shown at all.
@@ -164,6 +164,10 @@
         // save some items we use frequently
         final Intent intent = getIntent();
 
+        mSetOfAllowableAccounts = getAllowableAccountSet(intent);
+        mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);
+        mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
+
         if (savedInstanceState != null) {
             mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST);
             mExistingAccounts =
@@ -174,8 +178,15 @@
                     savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
             mSelectedAddNewAccount =
                     savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
-            mAccounts = (Map<Account, Integer>) savedInstanceState
-                    .getSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST);
+            // restore mAccounts
+            Parcelable[] accounts =
+                savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST);
+            ArrayList<Integer> visibility =
+                savedInstanceState.getIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST);
+            mAccounts = new LinkedHashMap<>();
+            for (int i = 0; i < accounts.length; i++) {
+                mAccounts.put((Account) accounts[i], visibility.get(i));
+            }
         } else {
             mPendingRequest = REQUEST_NULL;
             mExistingAccounts = null;
@@ -185,20 +196,21 @@
             if (selectedAccount != null) {
                 mSelectedAccountName = selectedAccount.name;
             }
+            mAccounts = getAcceptableAccountChoices(AccountManager.get(this));
         }
 
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "selected account name is " + mSelectedAccountName);
         }
 
+        mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size());
+        for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) {
+            if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) {
+                mPossiblyVisibleAccounts.add(entry.getKey());
+            }
+        }
 
-        mSetOfAllowableAccounts = getAllowableAccountSet(intent);
-        mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);
-        mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
-
-        mAccounts = getAcceptableAccountChoices(AccountManager.get(this));
-        if (mAccounts.isEmpty()
-                && mDisallowAddAccounts) {
+        if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) {
             requestWindowFeature(Window.FEATURE_NO_TITLE);
             setContentView(R.layout.app_not_authorized);
             mDontShowPicker = true;
@@ -216,7 +228,7 @@
         if (mPendingRequest == REQUEST_NULL) {
             // If there are no relevant accounts and only one relevant account type go directly to
             // add account. Otherwise let the user choose.
-            if (mAccounts.isEmpty()) {
+            if (mPossiblyVisibleAccounts.isEmpty()) {
                 setNonLabelThemeAndCallSuperCreate(savedInstanceState);
                 if (mSetOfRelevantAccountTypes.size() == 1) {
                     runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());
@@ -226,12 +238,6 @@
             }
         }
 
-        mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size());
-        for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) {
-            if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) {
-                mPossiblyVisibleAccounts.add(entry.getKey());
-            }
-        }
         String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts);
         mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName,
                 mSelectedAddNewAccount);
@@ -270,10 +276,16 @@
                         mPossiblyVisibleAccounts.get(mSelectedItemIndex).name);
             }
         }
-        // should be HashMap by default.
-        HashMap<Account, Integer> accountsHashMap = (mAccounts instanceof HashMap)
-                ? (HashMap) mAccounts : new HashMap<Account, Integer>(mAccounts);
-        outState.putSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST, accountsHashMap);
+        // save mAccounts
+        Parcelable[] accounts = new Parcelable[mAccounts.size()];
+        ArrayList<Integer> visibility = new ArrayList<>(mAccounts.size());
+        int i = 0;
+        for (Map.Entry<Account, Integer> e : mAccounts.entrySet()) {
+            accounts[i++] = e.getKey();
+            visibility.add(e.getValue());
+        }
+        outState.putParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST, accounts);
+        outState.putIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST, visibility);
     }
 
     public void onCancelButtonClicked(View view) {
@@ -308,7 +320,7 @@
         if (resultCode == RESULT_CANCELED) {
             // if canceling out of addAccount and the original state caused us to skip this,
             // finish this activity
-            if (mAccounts.isEmpty()) {
+            if (mPossiblyVisibleAccounts.isEmpty()) {
                 setResult(Activity.RESULT_CANCELED);
                 finish();
             }
@@ -428,18 +440,20 @@
     private void setResultAndFinish(final String accountName, final String accountType) {
         // Mark account as visible since user chose it.
         Account account = new Account(accountName, accountType);
-        Integer oldVisibility = mAccounts.get(account);
-        // oldVisibility is null if new account was added
-        if (oldVisibility == null) {
-            Map<Account, Integer> accountsAndVisibility = AccountManager.get(this)
-                    .getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
-            oldVisibility = accountsAndVisibility.get(account);
-        }
+        Integer oldVisibility =
+            AccountManager.get(this).getAccountVisibility(account, mCallingPackage);
         if (oldVisibility != null
                 && oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) {
             AccountManager.get(this).setAccountVisibility(account, mCallingPackage,
                     AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
         }
+
+        if (oldVisibility != null && oldVisibility == AccountManager.VISIBILITY_NOT_VISIBLE) {
+            // Added account is not visible to caller.
+            setResult(Activity.RESULT_CANCELED);
+            finish();
+            return;
+        }
         Bundle bundle = new Bundle();
         bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
         bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
@@ -448,6 +462,7 @@
             Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account "
                     + accountName + ", " + accountType);
         }
+
         finish();
     }
 
@@ -509,22 +524,24 @@
      * that don't match the allowable types, if provided, or that don't match the allowable
      * accounts, if provided.
      */
-    private Map<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) {
-        Map<Account, Integer> accountsAndVisibility =
-                accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
-
-        Map<Account, Integer> accountsToPopulate =
-                new HashMap<Account, Integer>(accountsAndVisibility.size());
-        for (Map.Entry<Account, Integer> entry : accountsAndVisibility.entrySet()) {
+    private LinkedHashMap<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) {
+        Map<Account, Integer> accountsAndVisibilityForCaller =
+                accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null);
+        Account[] allAccounts = accountManager.getAccounts();
+        LinkedHashMap<Account, Integer> accountsToPopulate =
+                new LinkedHashMap<>(accountsAndVisibilityForCaller.size());
+        for (Account account : allAccounts) {
             if (mSetOfAllowableAccounts != null
-                    && !mSetOfAllowableAccounts.contains(entry.getKey())) {
+                    && !mSetOfAllowableAccounts.contains(account)) {
                 continue;
             }
             if (mSetOfRelevantAccountTypes != null
-                    && !mSetOfRelevantAccountTypes.contains(entry.getKey().type)) {
+                    && !mSetOfRelevantAccountTypes.contains(account.type)) {
                 continue;
             }
-            accountsToPopulate.put(entry.getKey(), entry.getValue());
+            if (accountsAndVisibilityForCaller.get(account) != null) {
+                accountsToPopulate.put(account, accountsAndVisibilityForCaller.get(account));
+            }
         }
         return accountsToPopulate;
     }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d432160..2376a95 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -724,6 +724,7 @@
     public static final int FINISH_TASK_WITH_ACTIVITY = 2;
 
     static final String FRAGMENTS_TAG = "android:fragments";
+    static final String AUTOFILL_RESET_NEEDED_TAG = "android:autofillResetNeeded";
 
     private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
     private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
@@ -1057,6 +1058,12 @@
      * @see #onSaveInstanceState
      */
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED_TAG, false);
+
+        if (mAutoFillResetNeeded) {
+            getSystemService(AutofillManager.class).onRestoreInstanceState(savedInstanceState);
+        }
+
         if (mWindow != null) {
             Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
             if (windowState != null) {
@@ -1502,6 +1509,10 @@
         if (p != null) {
             outState.putParcelable(FRAGMENTS_TAG, p);
         }
+        if (mAutoFillResetNeeded) {
+            outState.putBoolean(AUTOFILL_RESET_NEEDED_TAG, mAutoFillResetNeeded);
+            getSystemService(AutofillManager.class).onSaveInstanceState(outState);
+        }
         getApplication().dispatchActivitySaveInstanceState(this, outState);
     }
 
@@ -2443,13 +2454,20 @@
     }
 
     /**
-     * Finds a view that was identified by the id attribute from the XML that
-     * was processed in {@link #onCreate}.
+     * Finds a view that was identified by the {@code android:id} XML attribute
+     * that was processed in {@link #onCreate}.
+     * <p>
+     * <strong>Note:</strong> In most cases -- depending on compiler support --
+     * the resulting view is automatically cast to the target class type. If
+     * the target class type is unconstrained, an explicit cast may be
+     * necessary.
      *
-     * @return The view if found or null otherwise.
+     * @param id the ID to search for
+     * @return a view with given ID if found, or {@code null} otherwise
+     * @see View#findViewById(int)
      */
     @Nullable
-    public View findViewById(@IdRes int id) {
+    public <T extends View> T findViewById(@IdRes int id) {
         return getWindow().findViewById(id);
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d1d462c..7299d6b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,6 +38,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.AssetManager;
@@ -869,16 +870,20 @@
             sendMessage(H.UNBIND_SERVICE, s);
         }
 
-        public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
-            int flags ,Intent args) {
-            ServiceArgsData s = new ServiceArgsData();
-            s.token = token;
-            s.taskRemoved = taskRemoved;
-            s.startId = startId;
-            s.flags = flags;
-            s.args = args;
+        public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
+            List<ServiceStartArgs> list = args.getList();
 
-            sendMessage(H.SERVICE_ARGS, s);
+            for (int i = 0; i < list.size(); i++) {
+                ServiceStartArgs ssa = list.get(i);
+                ServiceArgsData s = new ServiceArgsData();
+                s.token = token;
+                s.taskRemoved = ssa.taskRemoved;
+                s.startId = ssa.startId;
+                s.flags = ssa.flags;
+                s.args = ssa.args;
+
+                sendMessage(H.SERVICE_ARGS, s);
+            }
         }
 
         public final void scheduleStopService(IBinder token) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 72ccf72..943c572 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -498,14 +498,22 @@
     }
 
     /**
-     * Finds a child view with the given identifier. Returns null if the
-     * specified child view does not exist or the dialog has not yet been fully
-     * created (for example, via {@link #show()} or {@link #create()}).
+     * Finds the first descendant view with the given ID or {@code null} if the
+     * ID is invalid (< 0), there is no matching view in the hierarchy, or the
+     * dialog has not yet been fully created (for example, via {@link #show()}
+     * or {@link #create()}).
+     * <p>
+     * <strong>Note:</strong> In most cases -- depending on compiler support --
+     * the resulting view is automatically cast to the target class type. If
+     * the target class type is unconstrained, an explicit cast may be
+     * necessary.
      *
-     * @param id the identifier of the view to find
-     * @return The view with the given id or null.
+     * @param id the ID to search for
+     * @return a view with given ID if found, or {@code null} otherwise
+     * @see View#findViewById(int)
      */
-    public @Nullable View findViewById(@IdRes int id) {
+    @Nullable
+    public <T extends View> T findViewById(@IdRes int id) {
         return mWindow.findViewById(id);
     }
 
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 3102a93..a3c123f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -2608,6 +2608,12 @@
         }
     }
 
+    void noteStateNotSaved() {
+        if (mChildFragmentManager != null) {
+            mChildFragmentManager.noteStateNotSaved();
+        }
+    }
+
     @Deprecated
     void performMultiWindowModeChanged(boolean isInMultiWindowMode) {
         onMultiWindowModeChanged(isInMultiWindowMode);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 399987f..75d6295 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -314,14 +314,17 @@
     public abstract Fragment getFragment(Bundle bundle, String key);
 
     /**
-     * Get a collection of all fragments that are currently added to the FragmentManager.
+     * Get a list of all fragments that are currently added to the FragmentManager.
      * This may include those that are hidden as well as those that are shown.
      * This will not include any fragments only in the back stack, or fragments that
      * are detached or removed.
+     * <p>
+     * The order of the fragments in the list is the order in which they were
+     * added or attached.
      *
-     * @return A collection of all fragments that are added to the FragmentManager.
+     * @return A list of all fragments that are added to the FragmentManager.
      */
-    public abstract Collection<Fragment> getFragments();
+    public abstract List<Fragment> getFragments();
 
     /**
      * Save the current instance state of the given Fragment.  This can be
@@ -695,6 +698,9 @@
     // This is dangerous, but we want to keep from breaking old applications.
     boolean mAllowOldReentrantBehavior;
 
+    // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved()
+    FragmentManagerNonConfig mSavedNonConfig;
+
     Runnable mExecCommit = new Runnable() {
         @Override
         public void run() {
@@ -907,12 +913,12 @@
     }
 
     @Override
-    public Collection<Fragment> getFragments() {
+    public List<Fragment> getFragments() {
         if (mAdded == null) {
             return Collections.EMPTY_LIST;
         }
         synchronized (mAdded) {
-            return (Collection<Fragment>) mAdded.clone();
+            return (List<Fragment>) mAdded.clone();
         }
     }
 
@@ -2518,6 +2524,35 @@
     }
 
     FragmentManagerNonConfig retainNonConfig() {
+        setRetaining(mSavedNonConfig);
+        return mSavedNonConfig;
+    }
+
+    /**
+     * Recurse the FragmentManagerNonConfig fragments and set the mRetaining to true. This
+     * was previously done while saving the non-config state, but that has been moved to
+     * {@link #saveNonConfig()} called from {@link #saveAllState()}. If mRetaining is set too
+     * early, the fragment won't be destroyed when the FragmentManager is destroyed.
+     */
+    private static void setRetaining(FragmentManagerNonConfig nonConfig) {
+        if (nonConfig == null) {
+            return;
+        }
+        List<Fragment> fragments = nonConfig.getFragments();
+        if (fragments != null) {
+            for (Fragment fragment : fragments) {
+                fragment.mRetaining = true;
+            }
+        }
+        List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs();
+        if (children != null) {
+            for (FragmentManagerNonConfig child : children) {
+                setRetaining(child);
+            }
+        }
+    }
+
+    void saveNonConfig() {
         ArrayList<Fragment> fragments = null;
         ArrayList<FragmentManagerNonConfig> childFragments = null;
         if (mActive != null) {
@@ -2529,13 +2564,13 @@
                             fragments = new ArrayList<>();
                         }
                         fragments.add(f);
-                        f.mRetaining = true;
                         f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
                         if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                     }
                     boolean addedChild = false;
                     if (f.mChildFragmentManager != null) {
-                        FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
+                        f.mChildFragmentManager.saveNonConfig();
+                        FragmentManagerNonConfig child = f.mChildFragmentManager.mSavedNonConfig;
                         if (child != null) {
                             if (childFragments == null) {
                                 childFragments = new ArrayList<>();
@@ -2554,9 +2589,10 @@
             }
         }
         if (fragments == null && childFragments == null) {
-            return null;
+            mSavedNonConfig = null;
+        } else {
+            mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments);
         }
-        return new FragmentManagerNonConfig(fragments, childFragments);
     }
     
     void saveFragmentViewState(Fragment f) {
@@ -2617,6 +2653,7 @@
         execPendingActions();
 
         mStateSaved = true;
+        mSavedNonConfig = null;
 
         if (mActive == null || mActive.size() <= 0) {
             return null;
@@ -2717,6 +2754,7 @@
         if (mPrimaryNav != null) {
             fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
         }
+        saveNonConfig();
         return fms;
     }
     
@@ -2892,9 +2930,17 @@
     }
 
     public void noteStateNotSaved() {
+        mSavedNonConfig = null;
         mStateSaved = false;
+        final int addedCount = mAdded == null ? 0 : mAdded.size();
+        for (int i = 0; i < addedCount; i++) {
+            Fragment fragment = mAdded.get(i);
+            if (fragment != null) {
+                fragment.noteStateNotSaved();
+            }
+        }
     }
-    
+
     public void dispatchCreate() {
         mStateSaved = false;
         dispatchMoveToState(Fragment.CREATED);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f4d26fd..079bbcd 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -635,6 +635,11 @@
      */
     int getLastResumedActivityUserId();
 
+    /**
+     * Add a bare uid to the background restrictions whitelist.  Only the system uid may call this.
+     */
+     void backgroundWhitelistUid(int uid);
+
     // 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.
     // If a transaction which will also be used on the native side is being inserted, add it
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 6c43fe3..1b3c00b 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
@@ -86,8 +87,7 @@
             in Bundle coreSettings, in String buildSerial);
     void scheduleExit();
     void scheduleConfigurationChanged(in Configuration config);
-    void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
-            int flags, in Intent args);
+    void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
     void updateTimeZone();
     void processInBackground();
     void scheduleBindService(IBinder token,
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 489a0f0..d620a81 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -945,7 +945,7 @@
                 final ResourcesKey key = mResourceImpls.keyAt(i);
                 final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
                 final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
-                if (impl != null && key.mResDir.equals(assetPath)) {
+                if (impl != null && Objects.equals(key.mResDir, assetPath)) {
                     if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
                         final int newLibAssetCount = 1 +
                                 (key.mLibDirs != null ? key.mLibDirs.length : 0);
diff --git a/core/java/android/service/resolver/ResolverTarget.aidl b/core/java/android/app/ServiceStartArgs.aidl
similarity index 88%
rename from core/java/android/service/resolver/ResolverTarget.aidl
rename to core/java/android/app/ServiceStartArgs.aidl
index 6cab2d4..fd2cf0f 100644
--- a/core/java/android/service/resolver/ResolverTarget.aidl
+++ b/core/java/android/app/ServiceStartArgs.aidl
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-package android.service.resolver;
+package android.app;
 
-/**
- * @hide
- */
-parcelable ResolverTarget;
+/** @hide */
+parcelable ServiceStartArgs;
diff --git a/core/java/android/app/ServiceStartArgs.java b/core/java/android/app/ServiceStartArgs.java
new file mode 100644
index 0000000..f030cba
--- /dev/null
+++ b/core/java/android/app/ServiceStartArgs.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.app;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes a Service.onStartCommand() request from the system.
+ * @hide
+ */
+public class ServiceStartArgs implements Parcelable {
+    final public boolean taskRemoved;
+    final public int startId;
+    final public int flags;
+    final public Intent args;
+
+    public ServiceStartArgs(boolean _taskRemoved, int _startId, int _flags, Intent _args) {
+        taskRemoved = _taskRemoved;
+        startId = _startId;
+        flags = _flags;
+        args = _args;
+    }
+
+    public String toString() {
+        return "ServiceStartArgs{taskRemoved=" + taskRemoved + ", startId=" + startId
+                + ", flags=0x" + Integer.toHexString(flags) + ", args=" + args + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(taskRemoved ? 1 : 0);
+        out.writeInt(startId);
+        out.writeInt(flags);
+        if (args != null) {
+            out.writeInt(1);
+            args.writeToParcel(out, 0);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    public static final Parcelable.Creator<ServiceStartArgs> CREATOR
+            = new Parcelable.Creator<ServiceStartArgs>() {
+        public ServiceStartArgs createFromParcel(Parcel in) {
+            return new ServiceStartArgs(in);
+        }
+
+        public ServiceStartArgs[] newArray(int size) {
+            return new ServiceStartArgs[size];
+        }
+    };
+
+    public ServiceStartArgs(Parcel in) {
+        taskRemoved = in.readInt() != 0;
+        startId = in.readInt();
+        flags = in.readInt();
+        if (in.readInt() != 0) {
+            args = Intent.CREATOR.createFromParcel(in);
+        } else {
+            args = null;
+        }
+    }
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6b05bdf..4572578 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -74,7 +74,9 @@
 import android.net.EthernetManager;
 import android.net.IConnectivityManager;
 import android.net.IEthernetManager;
+import android.net.IIpSecService;
 import android.net.INetworkPolicyManager;
+import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
 import android.net.nsd.INsdManager;
@@ -261,6 +263,15 @@
                 return new ConnectivityManager(context, service);
             }});
 
+        registerService(Context.IPSEC_SERVICE, IpSecManager.class,
+                new StaticServiceFetcher<IpSecManager>() {
+            @Override
+            public IpSecManager createService() {
+                IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
+                IIpSecService service = IIpSecService.Stub.asInterface(b);
+                return new IpSecManager(service);
+            }});
+
         registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
                 new StaticServiceFetcher<CountryDetector>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bb5d830..82ad825 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2768,9 +2768,11 @@
      * or clears the lockscreen password.
      * <p>
      * <em>This token is highly sensitive and should be treated at the same level as user
-     * credentials. In particular, NEVER store this token on device in plaintext, especially in
-     * Device-Encrypted storage if the token will be used to reset password on FBE devices before
-     * user unlocks.
+     * credentials. In particular, NEVER store this token on device in plaintext. Do not store
+     * the plaintext token in device-encrypted storage if it will be needed to reset password on
+     * file-based encryption devices before user unlocks. Consider carefully how any password token
+     * will be stored on your server and who will need access to them. Tokens may be the subject of
+     * legal access requests.
      * </em>
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7d2db5c..545aef5 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -27,9 +27,6 @@
 import android.view.ViewStructure.HtmlInfo.Builder;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillType;
-import android.view.autofill.AutoFillValue;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
@@ -602,6 +599,10 @@
         boolean mSanitized;
         HtmlInfo mHtmlInfo;
 
+        // POJO used to override some autofill-related values when the node is parcelized.
+        // Not written to parcel.
+        AutofillOverlay mAutofillOverlay;
+
         int mX;
         int mY;
         int mScrollX;
@@ -759,6 +760,7 @@
             boolean writeSensitive = true;
 
             int flags = mFlags & ~FLAGS_ALL_CONTROL;
+
             if (mId != View.NO_ID) {
                 flags |= FLAGS_HAS_ID;
             }
@@ -813,6 +815,13 @@
                 // Remove 'checked' from sanitized autofill request.
                 writtenFlags = flags & ~FLAGS_CHECKED;
             }
+            if (mAutofillOverlay != null) {
+                if (mAutofillOverlay.focused) {
+                    writtenFlags |= ViewNode.FLAGS_FOCUSED;
+                } else {
+                    writtenFlags &= ~ViewNode.FLAGS_FOCUSED;
+                }
+            }
 
             out.writeInt(writtenFlags);
             if ((flags&FLAGS_HAS_ID) != 0) {
@@ -832,7 +841,14 @@
                 out.writeParcelable(mAutofillId, 0);
                 out.writeInt(mAutofillType);
                 out.writeStringArray(mAutofillHints);
-                final AutofillValue sanitizedValue = writeSensitive ? mAutofillValue : null;
+                final AutofillValue sanitizedValue;
+                if (mAutofillOverlay != null && mAutofillOverlay.value != null) {
+                    sanitizedValue = mAutofillOverlay.value;
+                } else if (writeSensitive) {
+                    sanitizedValue = mAutofillValue;
+                } else {
+                    sanitizedValue = null;
+                }
                 out.writeParcelable(sanitizedValue,  0);
                 out.writeStringArray(mAutofillOptions);
                 if (mHtmlInfo instanceof Parcelable) {
@@ -920,15 +936,6 @@
         }
 
         /**
-         * @hide
-         * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
-         */
-        @Deprecated
-        public AutoFillId getAutoFillId() {
-            return AutoFillId.forDaRealId(mAutofillId);
-        }
-
-        /**
          * Gets the id that can be used to autofill the view contents.
          *
          * <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not
@@ -939,26 +946,6 @@
         }
 
         /**
-         * @hide
-         * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype()
-         */
-        @Deprecated
-        public AutoFillType getAutoFillType() {
-            switch (getAutofillType()) {
-                case View.AUTOFILL_TYPE_TEXT:
-                    return AutoFillType.forText();
-                case View.AUTOFILL_TYPE_TOGGLE:
-                    return AutoFillType.forToggle();
-                case View.AUTOFILL_TYPE_LIST:
-                    return AutoFillType.forList();
-                case View.AUTOFILL_TYPE_DATE:
-                    return AutoFillType.forDate();
-                default:
-                    return null;
-            }
-        }
-
-        /**
          * Gets the the type of value that can be used to autofill the view contents.
          *
          * <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not
@@ -982,15 +969,6 @@
         }
 
         /**
-         * @hide
-         * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
-         */
-        @Deprecated
-        public AutoFillValue getAutoFillValue() {
-            return AutoFillValue.forDaRealValue(mAutofillValue);
-        }
-
-        /**
          * Gets the the value of this view.
          *
          * <p>It's only set when the {@link AssistStructure} is used for autofilling purposes, not
@@ -1000,6 +978,11 @@
             return mAutofillValue;
         }
 
+        /** @hide **/
+        public void setAutofillOverlay(AutofillOverlay overlay) {
+            mAutofillOverlay = overlay;
+        }
+
         /**
          * Gets the options that can be used to autofill this structure.
          *
@@ -1381,6 +1364,16 @@
         }
     }
 
+    /**
+     * POJO used to override some autofill-related values when the node is parcelized.
+     *
+     * @hide
+     */
+    static public class AutofillOverlay {
+        public boolean focused;
+        public AutofillValue value;
+    }
+
     static class ViewNodeBuilder extends ViewStructure {
         final AssistStructure mAssist;
         final ViewNode mNode;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 74a39e8..9f35e85 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -710,9 +710,9 @@
      * user may have a corporate profile. In this case the parent user profile has a
      * child profile, the corporate one.
      *
-     * @param profile The profile for which to get providers. Passing null is equivaled
-     *         to passing only the current user handle.
-     * @return The intalled providers.
+     * @param profile The profile for which to get providers. Passing null is equivalent
+     *        to querying for only the calling user.
+     * @return The installed providers.
      *
      * @see android.os.Process#myUserHandle()
      * @see android.os.UserManager#getUserProfiles()
@@ -722,7 +722,31 @@
             return Collections.emptyList();
         }
         return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
-                profile);
+                profile, null);
+    }
+
+    /**
+     * Gets the AppWidget providers for the given package and user profile. User
+     * profile can only be the current user or a profile of the current user. For
+     * example, the current user may have a corporate profile. In this case the
+     * parent user profile has a child profile, the corporate one.
+     *
+     * @param packageName The package for which to get providers. If null, this method is
+     *        equivalent to {@link #getInstalledProvidersForProfile(UserHandle)}.
+     * @param profile The profile for which to get providers. Passing null is equivalent
+     *        to querying for only the calling user.
+     * @return The installed providers.
+     *
+     * @see android.os.Process#myUserHandle()
+     * @see android.os.UserManager#getUserProfiles()
+     */
+    public List<AppWidgetProviderInfo> getInstalledProvidersForPackage(@Nullable String packageName,
+            @Nullable UserHandle profile) {
+        if (mService == null) {
+            return Collections.emptyList();
+        }
+        return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+                profile, packageName);
     }
 
     /**
@@ -733,7 +757,7 @@
             return Collections.emptyList();
         }
         return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
-                null);
+                null, null);
     }
 
     /**
@@ -752,7 +776,7 @@
         if (mService == null) {
             return Collections.emptyList();
         }
-        return getInstalledProvidersForProfile(categoryFilter, null);
+        return getInstalledProvidersForProfile(categoryFilter, null, null);
     }
 
     /**
@@ -766,6 +790,7 @@
      * @param profile A profile of the current user which to be queried. The user
      *        is itself also a profile. If null, the providers only for the current user
      *        are returned.
+     * @param packageName If specified, will only return providers from the given package.
      * @return The intalled providers.
      *
      * @see android.os.Process#myUserHandle()
@@ -774,7 +799,7 @@
      * @hide
      */
     public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
-            UserHandle profile) {
+            @Nullable UserHandle profile, @Nullable String packageName) {
         if (mService == null) {
             return Collections.emptyList();
         }
@@ -785,7 +810,7 @@
 
         try {
             ParceledListSlice<AppWidgetProviderInfo> providers = mService.getInstalledProvidersForProfile(
-                    categoryFilter, profile.getIdentifier());
+                    categoryFilter, profile.getIdentifier(), packageName);
             if (providers == null) {
                 return Collections.emptyList();
             }
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index c4e4e06..aaa5f19 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -50,6 +50,8 @@
 
     private final List<T> mList;
 
+    private int mInlineCountLimit = Integer.MAX_VALUE;
+
     public BaseParceledListSlice(List<T> list) {
         mList = list;
     }
@@ -135,6 +137,14 @@
     }
 
     /**
+     * Set a limit on the maximum number of entries in the array that will be included
+     * inline in the initial parcelling of this object.
+     */
+    public void setInlineCountLimit(int maxCount) {
+        mInlineCountLimit = maxCount;
+    }
+
+    /**
      * Write this to another Parcel. Note that this discards the internal Parcel
      * and should not be used anymore. This is so we can pass this to a Binder
      * where we won't have a chance to call recycle on this.
@@ -149,7 +159,7 @@
             final Class<?> listElementClass = mList.get(0).getClass();
             writeParcelableCreator(mList.get(0), dest);
             int i = 0;
-            while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
+            while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
                 dest.writeInt(1);
 
                 final T parcelable = mList.get(i);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9457d15..2dfb45f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6172,6 +6172,7 @@
             cpuAbiOverride = dest.readString();
             use32bitAbi = (dest.readInt() == 1);
             restrictUpdateHash = dest.createByteArray();
+            visibleToInstantApps = dest.readInt() == 1;
         }
 
         private static void internStringArrayList(List<String> list) {
@@ -6286,6 +6287,7 @@
             dest.writeString(cpuAbiOverride);
             dest.writeInt(use32bitAbi ? 1 : 0);
             dest.writeByteArray(restrictUpdateHash);
+            dest.writeInt(visibleToInstantApps ? 1 : 0);
         }
 
 
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 572a287..ea1d01b 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -131,6 +131,64 @@
      */
     public static final int TYPE_SAMPLING = 0x10004;
 
+    /**
+     * Local geo-magnetic Field.
+     *
+     * Additional into to sensor hardware.  Local geomagnetic field information based on
+     * device geo location. This type is primarily for for magnetic field calibration and rotation
+     * vector sensor fusion.
+     *
+     * float[3]: strength (uT), declination and inclination angle (rad).
+     * @hide
+     */
+    public static final int TYPE_LOCAL_GEOMAGNETIC_FIELD = 0x30000;
+
+    /**
+     * Local gravity acceleration strength.
+     *
+     * Additional info to sensor hardware for accelerometer calibration.
+     *
+     * float: gravitational acceleration norm in m/s^2.
+     * @hide
+     */
+    public static final int TYPE_LOCAL_GRAVITY = 0x30001;
+
+    /**
+     * Device dock state.
+     *
+     * Additional info to sensor hardware indicating dock states of device.
+     *
+     * int32_t: dock state following definition of {@link android.content.Intent#EXTRA_DOCK_STATE}.
+     *          Undefined values are ignored.
+     * @hide
+     */
+    public static final int TYPE_DOCK_STATE = 0x30002;
+
+    /**
+     * High performance mode.
+     *
+     * Additional info to sensor hardware. Device is able to use up more power and take more
+     * resources to improve throughput and latency in high performance mode. One possible use case
+     * is virtual reality, when sensor latency need to be carefully controlled.
+     *
+     * int32_t: 1 or 0, denoting device is in or out of high performance mode, respectively.
+     *          Other values are ignored.
+     * @hide
+     */
+    public static final int TYPE_HIGH_PERFORMANCE_MODE = 0x30003;
+
+    /**
+     * Magnetic field calibration hint.
+     *
+     * Additional info to sensor hardware. Device is notified when manually triggered magnetic field
+     * calibration procedure is started or stopped. The calibration procedure is assumed timed out
+     * after 1 minute from start, even if an explicit stop is not received.
+     *
+     * int32_t: 1 for calibration start, 0 for stop, other values are ignored.
+     * @hide
+     */
+    public static final int TYPE_MAGNETIC_FIELD_CALIBRATION = 0x30004;
+
     SensorAdditionalInfo(
             Sensor aSensor, int aType, int aSerial, int [] aIntValues, float [] aFloatValues) {
         sensor = aSensor;
@@ -139,4 +197,18 @@
         intValues = aIntValues;
         floatValues = aFloatValues;
     }
+
+    /** @hide */
+    public static SensorAdditionalInfo createLocalGeomagneticField(
+            float strength, float declination, float inclination) {
+        if (strength < 10 || strength > 100 // much beyond extreme values on earth
+                || declination < 0 || declination > Math.PI
+                || inclination < -Math.PI / 2 || inclination > Math.PI / 2) {
+            throw new IllegalArgumentException("Geomagnetic field info out of range");
+        }
+
+        return new SensorAdditionalInfo(
+                null, TYPE_LOCAL_GEOMAGNETIC_FIELD, 0,
+                null, new float[] { strength, declination, inclination});
+    }
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index a6930b0..1dc6478 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1927,4 +1927,12 @@
         }
         return delay;
     }
+
+    /** @hide */
+    public boolean setOperationParameter(SensorAdditionalInfo parameter) {
+        return setOperationParameterImpl(parameter);
+    }
+
+    /** @hide */
+    protected abstract boolean setOperationParameterImpl(SensorAdditionalInfo parameter);
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 7029847..0677179 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -67,6 +67,9 @@
     private static native int nativeConfigDirectChannel(
             long nativeInstance, int channelHandle, int sensorHandle, int rate);
 
+    private static native int nativeSetOperationParameter(
+            long nativeInstance, int type, float[] floatValues, int[] intValues);
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static boolean sNativeClassInited = false;
@@ -928,4 +931,9 @@
 
         }
     }
+
+    protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
+        return nativeSetOperationParameter(
+                mNativeInstance, parameter.type, parameter.floatValues, parameter.intValues) == 0;
+    }
 }
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
new file mode 100644
index 0000000..0aa3ce6
--- /dev/null
+++ b/core/java/android/net/IIpSecService.aidl
@@ -0,0 +1,46 @@
+/*
+** 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.net;
+
+import android.net.Network;
+import android.net.IpSecConfig;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IIpSecService
+{
+    Bundle reserveSecurityParameterIndex(
+            int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
+
+    void releaseSecurityParameterIndex(int resourceId);
+
+    Bundle openUdpEncapsulationSocket(int port, in IBinder binder);
+
+    void closeUdpEncapsulationSocket(in ParcelFileDescriptor socket);
+
+    Bundle createTransportModeTransform(in IpSecConfig c, in IBinder binder);
+
+    void deleteTransportModeTransform(int transformId);
+
+    void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+
+    void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index da5cb37..7fea4a2 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -164,6 +164,8 @@
 
     private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
         switch (algo) {
+            case ALGO_CRYPT_AES_CBC:
+                return (truncLenBits == 128 || truncLenBits == 192 || truncLenBits == 256);
             case ALGO_AUTH_HMAC_MD5:
                 return (truncLenBits >= 96 && truncLenBits <= 128);
             case ALGO_AUTH_HMAC_SHA1:
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index b58bf42..13dc19f 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -23,7 +23,7 @@
 
 /** @hide */
 public final class IpSecConfig implements Parcelable {
-    private static final String TAG = IpSecConfig.class.getSimpleName();
+    private static final String TAG = "IpSecConfig";
 
     //MODE_TRANSPORT or MODE_TUNNEL
     int mode;
@@ -43,13 +43,13 @@
         int spi;
 
         // Encryption Algorithm
-        IpSecAlgorithm encryptionAlgo;
+        IpSecAlgorithm encryption;
 
         // Authentication Algorithm
-        IpSecAlgorithm authenticationAlgo;
+        IpSecAlgorithm authentication;
     }
 
-    Flow[] flow = new Flow[2];
+    Flow[] flow = new Flow[] {new Flow(), new Flow()};
 
     // For tunnel mode IPv4 UDP Encapsulation
     // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
@@ -57,17 +57,15 @@
     int encapLocalPort;
     int encapRemotePort;
 
-    // An optional protocol to match with the selector
-    int selectorProto;
-
-    // A bitmask of FEATURE_* indicating which of the fields
-    // of this class are valid.
-    long features;
-
     // An interval, in seconds between the NattKeepalive packets
     int nattKeepaliveInterval;
 
-    public InetAddress getLocalIp() {
+    // Transport or Tunnel
+    public int getMode() {
+        return mode;
+    }
+
+    public InetAddress getLocalAddress() {
         return localAddress;
     }
 
@@ -75,19 +73,19 @@
         return flow[direction].spi;
     }
 
-    public InetAddress getRemoteIp() {
+    public InetAddress getRemoteAddress() {
         return remoteAddress;
     }
 
-    public IpSecAlgorithm getEncryptionAlgo(int direction) {
-        return flow[direction].encryptionAlgo;
+    public IpSecAlgorithm getEncryption(int direction) {
+        return flow[direction].encryption;
     }
 
-    public IpSecAlgorithm getAuthenticationAlgo(int direction) {
-        return flow[direction].authenticationAlgo;
+    public IpSecAlgorithm getAuthentication(int direction) {
+        return flow[direction].authentication;
     }
 
-    Network getNetwork() {
+    public Network getNetwork() {
         return network;
     }
 
@@ -103,18 +101,10 @@
         return encapRemotePort;
     }
 
-    public int getSelectorProto() {
-        return selectorProto;
-    }
-
-    int getNattKeepaliveInterval() {
+    public int getNattKeepaliveInterval() {
         return nattKeepaliveInterval;
     }
 
-    public boolean hasProperty(int featureBits) {
-        return (features & featureBits) == featureBits;
-    }
-
     // Parcelable Methods
 
     @Override
@@ -124,31 +114,25 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(features);
         // TODO: Use a byte array or other better method for storing IPs that can also include scope
         out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
         // TODO: Use a byte array or other better method for storing IPs that can also include scope
         out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
         out.writeParcelable(network, flags);
         out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryptionAlgo, flags);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authenticationAlgo, flags);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags);
         out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags);
-        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, flags);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags);
+        out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags);
         out.writeInt(encapType);
         out.writeInt(encapLocalPort);
         out.writeInt(encapRemotePort);
-        out.writeInt(selectorProto);
     }
 
     // Package Private: Used by the IpSecTransform.Builder;
     // there should be no public constructor for this object
-    IpSecConfig() {
-        flow[IpSecTransform.DIRECTION_IN].spi = 0;
-        flow[IpSecTransform.DIRECTION_OUT].spi = 0;
-        nattKeepaliveInterval = 0; //FIXME constant
-    }
+    IpSecConfig() {}
 
     private static InetAddress readInetAddressFromParcel(Parcel in) {
         String addrString = in.readString();
@@ -164,24 +148,22 @@
     }
 
     private IpSecConfig(Parcel in) {
-        features = in.readLong();
         localAddress = readInetAddressFromParcel(in);
         remoteAddress = readInetAddressFromParcel(in);
         network = (Network) in.readParcelable(Network.class.getClassLoader());
         flow[IpSecTransform.DIRECTION_IN].spi = in.readInt();
-        flow[IpSecTransform.DIRECTION_IN].encryptionAlgo =
+        flow[IpSecTransform.DIRECTION_IN].encryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        flow[IpSecTransform.DIRECTION_IN].authenticationAlgo =
+        flow[IpSecTransform.DIRECTION_IN].authentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt();
-        flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo =
+        flow[IpSecTransform.DIRECTION_OUT].encryption =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
-        flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo =
+        flow[IpSecTransform.DIRECTION_OUT].authentication =
                 (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
         encapType = in.readInt();
         encapLocalPort = in.readInt();
         encapRemotePort = in.readInt();
-        selectorProto = in.readInt();
     }
 
     public static final Parcelable.Creator<IpSecConfig> CREATOR =
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 2c544e9..6852beb 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,10 +17,11 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
-import android.annotation.SystemApi;
-import android.content.Context;
-import android.os.INetworkManagementService;
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.util.AndroidException;
 import dalvik.system.CloseGuard;
 import java.io.FileDescriptor;
@@ -41,6 +42,29 @@
     private static final String TAG = "IpSecManager";
 
     /**
+     * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
+     *
+     * <p>No IPsec packet may contain an SPI of 0.
+     */
+    public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
+
+    /** @hide */
+    public interface Status {
+        public static final int OK = 0;
+        public static final int RESOURCE_UNAVAILABLE = 1;
+        public static final int SPI_UNAVAILABLE = 2;
+    }
+
+    /** @hide */
+    public static final String KEY_STATUS = "status";
+    /** @hide */
+    public static final String KEY_RESOURCE_ID = "resourceId";
+    /** @hide */
+    public static final String KEY_SPI = "spi";
+    /** @hide */
+    public static final int INVALID_RESOURCE_ID = 0;
+
+    /**
      * Indicates that the combination of remote InetAddress and SPI was non-unique for a given
      * request. If encountered, selection of a new SPI is required before a transform may be
      * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random
@@ -79,42 +103,30 @@
         }
     }
 
-    private final Context mContext;
-    private final INetworkManagementService mService;
+    private final IIpSecService mService;
 
     public static final class SecurityParameterIndex implements AutoCloseable {
-        private final Context mContext;
-        private final InetAddress mDestinationAddress;
+        private final IIpSecService mService;
+        private final InetAddress mRemoteAddress;
         private final CloseGuard mCloseGuard = CloseGuard.get();
-        private int mSpi;
+        private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
+        private int mResourceId;
 
         /** Return the underlying SPI held by this object */
         public int getSpi() {
             return mSpi;
         }
 
-        private SecurityParameterIndex(Context context, InetAddress destinationAddress, int spi)
-                throws ResourceUnavailableException, SpiUnavailableException {
-            mContext = context;
-            mDestinationAddress = destinationAddress;
-            mSpi = spi;
-            mCloseGuard.open("open");
-        }
-
         /**
          * Release an SPI that was previously reserved.
          *
-         * <p>Release an SPI for use by other users in the system. This will fail if the SPI is
-         * currently in use by an IpSecTransform.
-         *
-         * @param destinationAddress SPIs must be unique for each combination of SPI and destination
-         *     address. Thus, the destinationAddress to which the SPI will communicate must be
-         *     supplied.
-         * @param spi the previously reserved SPI to be freed.
+         * <p>Release an SPI for use by other users in the system. If a SecurityParameterIndex is
+         * applied to an IpSecTransform, it will become unusable for future transforms but should
+         * still be closed to ensure system resources are released.
          */
         @Override
         public void close() {
-            mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI
+            mSpi = INVALID_SECURITY_PARAMETER_INDEX;
             mCloseGuard.close();
         }
 
@@ -126,23 +138,61 @@
 
             close();
         }
+
+        private SecurityParameterIndex(
+                @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
+                throws ResourceUnavailableException, SpiUnavailableException {
+            mService = service;
+            mRemoteAddress = remoteAddress;
+            try {
+                Bundle result =
+                        mService.reserveSecurityParameterIndex(
+                                direction, remoteAddress.getHostAddress(), spi, new Binder());
+
+                if (result == null) {
+                    throw new NullPointerException("Received null response from IpSecService");
+                }
+
+                int status = result.getInt(KEY_STATUS);
+                switch (status) {
+                    case Status.OK:
+                        break;
+                    case Status.RESOURCE_UNAVAILABLE:
+                        throw new ResourceUnavailableException(
+                                "No more SPIs may be allocated by this requester.");
+                    case Status.SPI_UNAVAILABLE:
+                        throw new SpiUnavailableException("Requested SPI is unavailable", spi);
+                    default:
+                        throw new RuntimeException(
+                                "Unknown status returned by IpSecService: " + status);
+                }
+                mSpi = result.getInt(KEY_SPI);
+                mResourceId = result.getInt(KEY_RESOURCE_ID);
+
+                if (mSpi == INVALID_SECURITY_PARAMETER_INDEX) {
+                    throw new RuntimeException("Invalid SPI returned by IpSecService: " + status);
+                }
+
+                if (mResourceId == INVALID_RESOURCE_ID) {
+                    throw new RuntimeException(
+                            "Invalid Resource ID returned by IpSecService: " + status);
+                }
+
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mCloseGuard.open("open");
+        }
     }
 
     /**
-     * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
-     *
-     * <p>No IPsec packet may contain an SPI of 0.
-     */
-    public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
-
-    /**
-     * Reserve an SPI for traffic bound towards the specified destination address.
+     * Reserve an SPI for traffic bound towards the specified remote address.
      *
      * <p>If successful, this SPI is guaranteed available until released by a call to {@link
      * SecurityParameterIndex#close()}.
      *
-     * @param destinationAddress SPIs must be unique for each combination of SPI and destination
-     *     address.
+     * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
+     * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress.
      * @param requestedSpi the requested SPI, or '0' to allocate a random SPI.
      * @return the reserved SecurityParameterIndex
      * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
@@ -150,9 +200,9 @@
      * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
      */
     public SecurityParameterIndex reserveSecurityParameterIndex(
-            InetAddress destinationAddress, int requestedSpi)
+            int direction, InetAddress remoteAddress, int requestedSpi)
             throws SpiUnavailableException, ResourceUnavailableException {
-        return new SecurityParameterIndex(mContext, destinationAddress, requestedSpi);
+        return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi);
     }
 
     /**
@@ -190,7 +240,13 @@
     }
 
     /* Call down to activate a transform */
-    private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
+    private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
+        try {
+            mService.applyTransportModeTransform(pfd, transform.getResourceId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
@@ -203,7 +259,6 @@
      * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
      * @hide
      */
-    @SystemApi
     public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
 
     /**
@@ -235,7 +290,13 @@
     }
 
     /* Call down to activate a transform */
-    private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
+    private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
+        try {
+            mService.removeTransportModeTransform(pfd, transform.getResourceId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
@@ -248,7 +309,6 @@
      *     network
      * @hide
      */
-    @SystemApi
     public void removeTunnelModeTransform(Network net, IpSecTransform transform) {}
 
     /**
@@ -260,19 +320,19 @@
      */
     public static final class UdpEncapsulationSocket implements AutoCloseable {
         private final FileDescriptor mFd;
-        private final Context mContext;
+        private final IIpSecService mService;
         private final CloseGuard mCloseGuard = CloseGuard.get();
 
-        private UdpEncapsulationSocket(Context context, int port)
+        private UdpEncapsulationSocket(@NonNull IIpSecService service, int port)
                 throws ResourceUnavailableException {
-            mContext = context;
+            mService = service;
             mCloseGuard.open("constructor");
             // TODO: go down to the kernel and get a socket on the specified
             mFd = new FileDescriptor();
         }
 
-        private UdpEncapsulationSocket(Context context) throws ResourceUnavailableException {
-            mContext = context;
+        private UdpEncapsulationSocket(IIpSecService service) throws ResourceUnavailableException {
+            mService = service;
             mCloseGuard.open("constructor");
             // TODO: go get a random socket on a random port
             mFd = new FileDescriptor();
@@ -339,7 +399,7 @@
     public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
             throws IOException, ResourceUnavailableException {
         // Temporary code
-        return new UdpEncapsulationSocket(mContext, port);
+        return new UdpEncapsulationSocket(mService, port);
     }
 
     /**
@@ -363,7 +423,7 @@
     public UdpEncapsulationSocket openUdpEncapsulationSocket()
             throws IOException, ResourceUnavailableException {
         // Temporary code
-        return new UdpEncapsulationSocket(mContext);
+        return new UdpEncapsulationSocket(mService);
     }
 
     /**
@@ -372,8 +432,7 @@
      * @param context the application context for this manager
      * @hide
      */
-    public IpSecManager(Context context, INetworkManagementService service) {
-        mContext = checkNotNull(context, "missing context");
+    public IpSecManager(IIpSecService service) {
         mService = checkNotNull(service, "missing service");
     }
 }
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index d6dd28b..801e98c 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -15,11 +15,21 @@
  */
 package android.net;
 
+import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static android.net.IpSecManager.KEY_RESOURCE_ID;
+import static android.net.IpSecManager.KEY_STATUS;
+
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.Context;
-import android.system.ErrnoException;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
+import com.android.internal.util.Preconditions;
 import dalvik.system.CloseGuard;
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -86,39 +96,64 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EncapType {}
 
-    /**
-     * Sentinel for an invalid transform (means that this transform is inactive).
-     *
-     * @hide
-     */
-    public static final int INVALID_TRANSFORM_ID = -1;
-
     private IpSecTransform(Context context, IpSecConfig config) {
         mContext = context;
         mConfig = config;
-        mTransformId = INVALID_TRANSFORM_ID;
+        mResourceId = INVALID_RESOURCE_ID;
+    }
+
+    private IIpSecService getIpSecService() {
+        IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
+        if (b == null) {
+            throw new RemoteException("Failed to connect to IpSecService")
+                    .rethrowAsRuntimeException();
+        }
+
+        return IIpSecService.Stub.asInterface(b);
+    }
+
+    private void checkResultStatusAndThrow(int status)
+            throws IOException, IpSecManager.ResourceUnavailableException,
+                    IpSecManager.SpiUnavailableException {
+        switch (status) {
+            case IpSecManager.Status.OK:
+                return;
+                // TODO: Pass Error string back from bundle so that errors can be more specific
+            case IpSecManager.Status.RESOURCE_UNAVAILABLE:
+                throw new IpSecManager.ResourceUnavailableException(
+                        "Failed to allocate a new IpSecTransform");
+            case IpSecManager.Status.SPI_UNAVAILABLE:
+                Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved");
+                // Fall through
+            default:
+                throw new IllegalStateException(
+                        "Failed to Create a Transform with status code " + status);
+        }
     }
 
     private IpSecTransform activate()
             throws IOException, IpSecManager.ResourceUnavailableException,
                     IpSecManager.SpiUnavailableException {
-        int transformId;
         synchronized (this) {
-            //try {
-            transformId = INVALID_TRANSFORM_ID;
-            //} catch (RemoteException e) {
-            //    throw e.rethrowFromSystemServer();
-            //}
+            try {
+                IIpSecService svc = getIpSecService();
+                Bundle result = svc.createTransportModeTransform(mConfig, new Binder());
+                int status = result.getInt(KEY_STATUS);
+                checkResultStatusAndThrow(status);
+                mResourceId = result.getInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID);
 
-            if (transformId < 0) {
-                throw new ErrnoException("addTransform", -transformId).rethrowAsIOException();
+                /* Keepalive will silently fail if not needed by the config; but, if needed and
+                 * it fails to start, we need to bail because a transform will not be reliable
+                 * to use if keepalive is expected to offload and fails.
+                 */
+                // FIXME: if keepalive fails, we need to fail spectacularly
+                startKeepalive(mContext);
+                Log.d(TAG, "Added Transform with Id " + mResourceId);
+                mCloseGuard.open("build");
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
             }
-
-            startKeepalive(mContext); // Will silently fail if not required
-            mTransformId = transformId;
-            Log.d(TAG, "Added Transform with Id " + transformId);
         }
-        mCloseGuard.open("build");
 
         return this;
     }
@@ -133,21 +168,27 @@
      * transform is no longer needed.
      */
     public void close() {
-        Log.d(TAG, "Removing Transform with Id " + mTransformId);
+        Log.d(TAG, "Removing Transform with Id " + mResourceId);
 
         // Always safe to attempt cleanup
-        if (mTransformId == INVALID_TRANSFORM_ID) {
+        if (mResourceId == INVALID_RESOURCE_ID) {
+            mCloseGuard.close();
             return;
         }
-        //try {
-        stopKeepalive();
-        //} catch (RemoteException e) {
-        //    transform.setTransformId(transformId);
-        //    throw e.rethrowFromSystemServer();
-        //} finally {
-        mTransformId = INVALID_TRANSFORM_ID;
-        //}
-        mCloseGuard.close();
+        try {
+            /* Order matters here because the keepalive is best-effort but could fail in some
+             * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
+             * still want to clear out the transform.
+             */
+            IIpSecService svc = getIpSecService();
+            svc.deleteTransportModeTransform(mResourceId);
+            stopKeepalive();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } finally {
+            mResourceId = INVALID_RESOURCE_ID;
+            mCloseGuard.close();
+        }
     }
 
     @Override
@@ -164,7 +205,7 @@
     }
 
     private final IpSecConfig mConfig;
-    private int mTransformId;
+    private int mResourceId;
     private final Context mContext;
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private ConnectivityManager.PacketKeepalive mKeepalive;
@@ -200,6 +241,7 @@
 
     /* Package */
     void startKeepalive(Context c) {
+        // FIXME: NO_KEEPALIVE needs to be a constant
         if (mConfig.getNattKeepaliveInterval() == 0) {
             return;
         }
@@ -208,7 +250,7 @@
                 (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
 
         if (mKeepalive != null) {
-            Log.e(TAG, "Keepalive already started for this IpSecTransform.");
+            Log.wtf(TAG, "Keepalive already started for this IpSecTransform.");
             return;
         }
 
@@ -218,10 +260,11 @@
                             mConfig.getNetwork(),
                             mConfig.getNattKeepaliveInterval(),
                             mKeepaliveCallback,
-                            mConfig.getLocalIp(),
+                            mConfig.getLocalAddress(),
                             mConfig.getEncapLocalPort(),
-                            mConfig.getRemoteIp());
+                            mConfig.getRemoteAddress());
             try {
+                // FIXME: this is still a horrible way to fudge the synchronous callback
                 mKeepaliveSyncLock.wait(2000);
             } catch (InterruptedException e) {
             }
@@ -232,6 +275,11 @@
     }
 
     /* Package */
+    int getResourceId() {
+        return mResourceId;
+    }
+
+    /* Package */
     void stopKeepalive() {
         if (mKeepalive == null) {
             return;
@@ -247,16 +295,6 @@
         }
     }
 
-    /* Package */
-    void setTransformId(int transformId) {
-        mTransformId = transformId;
-    }
-
-    /* Package */
-    int getTransformId() {
-        return mTransformId;
-    }
-
     /**
      * Builder object to facilitate the creation of IpSecTransform objects.
      *
@@ -280,7 +318,7 @@
          */
         public IpSecTransform.Builder setEncryption(
                 @TransformDirection int direction, IpSecAlgorithm algo) {
-            mConfig.flow[direction].encryptionAlgo = algo;
+            mConfig.flow[direction].encryption = algo;
             return this;
         }
 
@@ -295,7 +333,7 @@
          */
         public IpSecTransform.Builder setAuthentication(
                 @TransformDirection int direction, IpSecAlgorithm algo) {
-            mConfig.flow[direction].authenticationAlgo = algo;
+            mConfig.flow[direction].authentication = algo;
             return this;
         }
 
@@ -305,32 +343,9 @@
          * given destination address.
          *
          * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
-         * possible. Random number generation is a reasonable approach to selecting an SPI. For
-         * outbound SPIs, they must be reserved by calling {@link
-         * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
-         * fail to build.
-         *
-         * <p>Unless an SPI is set for a given direction, traffic in that direction will be
-         * sent/received without any IPsec applied.
-         *
-         * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
-         * @param spi a unique 32-bit integer to identify transformed traffic
-         */
-        public IpSecTransform.Builder setSpi(@TransformDirection int direction, int spi) {
-            mConfig.flow[direction].spi = spi;
-            return this;
-        }
-
-        /**
-         * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
-         * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
-         * given destination address.
-         *
-         * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
-         * possible. Random number generation is a reasonable approach to selecting an SPI. For
-         * outbound SPIs, they must be reserved by calling {@link
-         * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
-         * fail to activate.
+         * possible. To reserve a value call {@link IpSecManager#reserveSecurityParameterIndex(int,
+         * InetAddress, int)}. Otherwise, SPI collisions would prevent a transform from being
+         * activated. IpSecManager#reserveSecurityParameterIndex(int, InetAddres$s, int)}.
          *
          * <p>Unless an SPI is set for a given direction, traffic in that direction will be
          * sent/received without any IPsec applied.
@@ -341,6 +356,8 @@
          */
         public IpSecTransform.Builder setSpi(
                 @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
+            // TODO: convert to using the resource Id of the SPI. Then build() can validate
+            // the owner in the IpSecService
             mConfig.flow[direction].spi = spi.getSpi();
             return this;
         }
@@ -447,7 +464,6 @@
          *     properties is invalid.
          * @hide
          */
-        @SystemApi
         public IpSecTransform buildTunnelModeTransform(
                 InetAddress localAddress, InetAddress remoteAddress) {
             //FIXME: argument validation here
@@ -463,7 +479,8 @@
          *
          * @param context current Context
          */
-        public Builder(Context context) {
+        public Builder(@NonNull Context context) {
+            Preconditions.checkNotNull(context);
             mContext = context;
             mConfig = new IpSecConfig();
         }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dd11f68..4c6d22a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -182,7 +182,7 @@
      * New in version 19:
      *   - Wakelock data (wl) gets current and max times.
      * New in version 20:
-     *   - Sensor, BluetoothScan, WifiScan get background timers and counter.
+     *   - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs.
      */
     static final String CHECKIN_VERSION = "20";
 
@@ -392,6 +392,16 @@
         }
 
         /**
+         * Returns the secondary Timer held by the Timer, if one exists. This secondary timer may be
+         * used, for example, for tracking background usage. Secondary timers are never pooled.
+         *
+         * Not all Timer subclasses have a secondary timer; those that don't return null.
+         */
+        public Timer getSubTimer() {
+            return null;
+        }
+
+        /**
          * Returns whether the timer is currently running.  Some types of timers
          * (e.g. BatchTimers) don't know whether the event is currently active,
          * and report false.
@@ -3303,16 +3313,17 @@
             final int wifiScanCount = u.getWifiScanCount(which);
             final int wifiScanCountBg = u.getWifiScanBackgroundCount(which);
             // Note that 'ActualTime' are unpooled and always since reset (regardless of 'which')
-            final long wifiScanActualTime = u.getWifiScanActualTime(rawRealtime);
-            final long wifiScanActualTimeBg = u.getWifiScanBackgroundTime(rawRealtime);
+            final long wifiScanActualTimeMs = (u.getWifiScanActualTime(rawRealtime) + 500) / 1000;
+            final long wifiScanActualTimeMsBg = (u.getWifiScanBackgroundTime(rawRealtime) + 500)
+                    / 1000;
             final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
             if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0
-                    || wifiScanCountBg != 0 || wifiScanActualTime != 0 || wifiScanActualTimeBg != 0
-                    || uidWifiRunningTime != 0) {
+                    || wifiScanCountBg != 0 || wifiScanActualTimeMs != 0
+                    || wifiScanActualTimeMsBg != 0 || uidWifiRunningTime != 0) {
                 dumpLine(pw, uid, category, WIFI_DATA, fullWifiLockOnTime, wifiScanTime,
                         uidWifiRunningTime, wifiScanCount,
                         /* legacy fields follow, keep at 0 */ 0, 0, 0,
-                        wifiScanCountBg, wifiScanActualTime, wifiScanActualTimeBg);
+                        wifiScanCountBg, wifiScanActualTimeMs, wifiScanActualTimeMsBg);
             }
 
             dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA,
@@ -3393,9 +3404,13 @@
                 // Convert from microseconds to milliseconds with rounding
                 final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
                 final int count = timer.getCountLocked(which);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTime = bgTimer != null ?
+                        (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1;
+                final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
                 if (totalTime != 0) {
                     dumpLine(pw, uid, category, JOB_DATA, "\"" + jobs.keyAt(ij) + "\"",
-                            totalTime, count);
+                            totalTime, count, bgTime, bgCount);
                 }
             }
 
@@ -4616,6 +4631,10 @@
                 // Convert from microseconds to milliseconds with rounding
                 final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
                 final int count = timer.getCountLocked(which);
+                final Timer bgTimer = timer.getSubTimer();
+                final long bgTime = bgTimer != null ?
+                        (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1;
+                final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1;
                 sb.setLength(0);
                 sb.append(prefix);
                 sb.append("    Job ");
@@ -4626,6 +4645,13 @@
                     sb.append("realtime (");
                     sb.append(count);
                     sb.append(" times)");
+                    if (bgTime > 0) {
+                        sb.append(", ");
+                        formatTimeMs(sb, bgTime);
+                        sb.append("background (");
+                        sb.append(bgCount);
+                        sb.append(" times)");
+                    }
                 } else {
                     sb.append("(not used)");
                 }
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
deleted file mode 100644
index c26f679..0000000
--- a/core/java/android/service/autofill/AutoFillService.java
+++ /dev/null
@@ -1,24 +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;
-
-/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use AutofillService
- */
-@Deprecated
-public abstract class AutoFillService extends AutofillService {
-}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 6f17d0e..ef14095 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -71,7 +71,7 @@
      * Name under which a AutoFillService component publishes information about itself.
      * This meta-data should reference an XML resource containing a
      * <code>&lt;{@link
-     * android.R.styleable#AutoFillService autofill-service}&gt;</code> tag.
+     * android.R.styleable#AutofillService autofill-service}&gt;</code> tag.
      * This is a a sample XML file configuring an AutoFillService:
      * <pre> &lt;autofill-service
      *     android:settingsActivity="foo.bar.SettingsActivity"
@@ -204,11 +204,12 @@
      * 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.
+     * @param data bundle containing data passed by the service in a last call to
+     *        {@link FillResponse.Builder#setExtras(Bundle)}, if any. 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} for examples of multiple-sections requests.
      * @param flags either {@code 0} or {@link AutofillManager#FLAG_MANUAL_REQUEST}.
      * @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
@@ -242,11 +243,12 @@
      * 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.
+     * @param data bundle containing data passed by the service in a last call to
+     *        {@link FillResponse.Builder#setExtras(Bundle)}, if any. 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} 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/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index f6d40db..0f4824e 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -36,7 +36,6 @@
 
 import java.io.IOException;
 
-// TODO(b/33197203 , b/33802548): add CTS tests
 /**
  * {@link ServiceInfo} and meta-data about an {@link AutofillService}.
  *
@@ -75,15 +74,8 @@
         mServiceInfo = si;
         final TypedArray metaDataArray = getMetaDataArray(pm, si);
         if (metaDataArray != null) {
-            // TODO(b/35956626): inline newSettingsActivity once clients migrate
-            final String newSettingsActivity =
-                    metaDataArray.getString(R.styleable.AutofillService_settingsActivity);
-            if (newSettingsActivity != null) {
-                mSettingsActivity = newSettingsActivity;
-            } else {
-                mSettingsActivity =
-                        metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
-            }
+            mSettingsActivity = metaDataArray
+                    .getString(R.styleable.AutofillService_settingsActivity);
             metaDataArray.recycle();
         } else {
             mSettingsActivity = null;
@@ -96,16 +88,11 @@
     @Nullable
     private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
         // Check for permissions.
-        // TODO(b/35956626): remove check for BIND_AUTO_FILL once clients migrate
-        if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission)
-                && !Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
+        if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission)) {
             Log.e(TAG, "Service does not require permission " + Manifest.permission.BIND_AUTOFILL);
             return null;
         }
 
-        // TODO(b/35956626): remove once clients migrate
-        final boolean oldStyle = !Manifest.permission.BIND_AUTOFILL.equals(si.permission);
-
         // Get the AutoFill metadata, if declared.
         XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA);
         if (parser == null) {
@@ -141,8 +128,7 @@
                 return null;
             }
 
-            return oldStyle ? res.obtainAttributes(attrs, R.styleable.AutoFillService)
-                    : res.obtainAttributes(attrs, R.styleable.AutofillService);
+            return res.obtainAttributes(attrs, R.styleable.AutofillService);
         } finally {
             parser.close();
         }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index b7a0420..e77bd0d 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -23,8 +23,6 @@
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillValue;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.widget.RemoteViews;
@@ -175,15 +173,6 @@
         }
 
         /**
-         * @hide
-         * @deprecated TODO(b/35956626): remove once clients use other setValue()
-         */
-       @Deprecated
-        public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
-            return setValue(id.getDaRealId(), value.getDaRealValue());
-        }
-
-        /**
          * Sets the value of a field.
          *
          * @param id id returned by {@link
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index c43019d..3117f98 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.service.autofill;
 
 import static android.view.autofill.Helper.DEBUG;
@@ -23,6 +24,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.widget.RemoteViews;
 
@@ -112,7 +114,7 @@
  *
  * <p>The service could require user authentication at the {@link FillResponse} or the
  * {@link Dataset} level, prior to autofilling an activity - see
- * {@link FillResponse.Builder#setAuthentication(IntentSender, RemoteViews)} and
+ * {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews)} and
  * {@link Dataset.Builder#setAuthentication(IntentSender)}.
  *
  * <p>It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
@@ -126,7 +128,7 @@
  * possible options) which will start your auth flow and after successfully authenticating
  * the user will be presented with the Home and Work options to 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>
+ * encrypted provides a better user experience.
  */
 public final class FillResponse implements Parcelable {
 
@@ -135,6 +137,7 @@
     private final Bundle mExtras;
     private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
+    private AutofillId[] mAuthenticationIds;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = builder.mDatasets;
@@ -142,6 +145,7 @@
         mExtras = builder.mExtras;
         mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
+        mAuthenticationIds = builder.mAuthenticationIds;
     }
 
     /** @hide */
@@ -169,6 +173,11 @@
         return mAuthentication;
     }
 
+    /** @hide */
+    public @Nullable AutofillId[] getAuthenticationIds() {
+        return mAuthenticationIds;
+    }
+
     /**
      * Builder for {@link FillResponse} objects. You must to provide at least
      * one dataset or set an authentication intent with a presentation view.
@@ -179,6 +188,7 @@
         private Bundle mExtras;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
+        private AutofillId[] mAuthenticationIds;
         private boolean mDestroyed;
 
         /**
@@ -193,7 +203,7 @@
          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
          * activity which implements your authentication flow. Also if you provide an auth
          * intent you also need to specify the presentation view to be shown in the fill UI
-         * for the user to trigger your authentication flow.</p>
+         * for the user to trigger your authentication flow.
          *
          * <p>When a user triggers autofill, the system launches the provided intent
          * whose extras will have the {@link AutofillManager#EXTRA_ASSIST_STRUCTURE screen
@@ -205,40 +215,54 @@
          * 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>
+         * example a credit card whose CVV needs to be entered.
          *
          * <p>If you provide an authentication intent you must also provide a presentation
          * which is used to visualize visualize the response for triggering the authentication
-         * flow.</p>
+         * flow.
          *
          * <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>
+         * platform needs to fill in the authentication arguments.
          *
          * @param authentication Intent to an activity with your authentication flow.
          * @param presentation The presentation to visualize the response.
-         * @return This builder.
+         * @param ids id of Views that when focused will display the authentication UI affordance.
          *
+         * @return This builder.
          * @see android.app.PendingIntent#getIntentSender()
          */
-        public @NonNull Builder setAuthentication(@Nullable IntentSender authentication,
-                @Nullable RemoteViews presentation) {
+        public @NonNull Builder setAuthentication(@NonNull AutofillId[] ids,
+                @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
             throwIfDestroyed();
+            // TODO(b/33197203): assert ids is not null nor empty once old version is removed
             if (authentication == null ^ presentation == null) {
                 throw new IllegalArgumentException("authentication and presentation"
                         + " must be both non-null or null");
             }
             mAuthentication = authentication;
             mPresentation = presentation;
+            mAuthenticationIds = ids;
             return this;
         }
 
         /**
+         * TODO(b/33197203): will be removed once clients use the version that takes ids
+         * @hide
+         * @deprecated
+         */
+        @Deprecated
+        public @NonNull Builder setAuthentication(@Nullable IntentSender authentication,
+                @Nullable RemoteViews presentation) {
+            return setAuthentication(null, authentication, presentation);
+        }
+
+        /**
          * Adds a new {@link Dataset} to this response.
          *
          * @return This builder.
          */
-        public@NonNull Builder addDataset(@Nullable Dataset dataset) {
+        public @NonNull Builder addDataset(@Nullable Dataset dataset) {
             throwIfDestroyed();
             if (dataset == null) {
                 return this;
@@ -273,6 +297,9 @@
          * android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest(
          * android.app.assist.AssistStructure, Bundle, SaveCallback)}.
          *
+         * <p>If this method is called on multiple {@link FillResponse} objects for the same
+         * activity, just the latest bundle is passed back to the service.
+         *
          * @param extras The response extras.
          * @return This builder.
          */
@@ -282,6 +309,7 @@
             return this;
         }
 
+
         /**
          * Builds a new {@link FillResponse} instance. You must provide at least
          * one dataset or some savable ids or an authentication with a presentation
@@ -308,7 +336,7 @@
     }
 
     /////////////////////////////////////
-    //  Object "contract" methods. //
+    // Object "contract" methods. //
     /////////////////////////////////////
     @Override
     public String toString() {
@@ -320,11 +348,13 @@
                 .append(", hasExtras=").append(mExtras != null)
                 .append(", hasPresentation=").append(mPresentation != null)
                 .append(", hasAuthentication=").append(mAuthentication != null)
+                .append(", authenticationSize=").append(mAuthenticationIds != null
+                        ? mAuthenticationIds.length : "N/A")
                 .toString();
     }
 
     /////////////////////////////////////
-    //  Parcelable "contract" methods. //
+    // Parcelable "contract" methods. //
     /////////////////////////////////////
 
     @Override
@@ -337,6 +367,7 @@
         parcel.writeTypedArrayList(mDatasets, flags);
         parcel.writeParcelable(mSaveInfo, flags);
         parcel.writeParcelable(mExtras, flags);
+        parcel.writeParcelableArray(mAuthenticationIds, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeParcelable(mPresentation, flags);
     }
@@ -356,8 +387,8 @@
             }
             builder.setSaveInfo(parcel.readParcelable(null));
             builder.setExtras(parcel.readParcelable(null));
-            builder.setAuthentication(parcel.readParcelable(null),
-                    parcel.readParcelable(null));
+            builder.setAuthentication(parcel.readParcelableArray(null, AutofillId.class),
+                    parcel.readParcelable(null), parcel.readParcelable(null));
             return builder.build();
         }
 
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 6213d27..f75b7af 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,7 +25,6 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.view.autofill.AutoFillId;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
@@ -130,6 +129,16 @@
      */
     public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3;
 
+    /**
+     * Type used when the {@link FillResponse} represents just an username, without a password.
+     */
+    public static final int SAVE_DATA_TYPE_USERNAME = 4;
+
+    /**
+     * Type used when the {@link FillResponse} represents just an email address, without a password.
+     */
+    public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5;
+
     private final @SaveDataType int mType;
     private final CharSequence mNegativeActionTitle;
     private final IntentSender mNegativeActionListener;
@@ -223,6 +232,8 @@
                 case SAVE_DATA_TYPE_PASSWORD:
                 case SAVE_DATA_TYPE_ADDRESS:
                 case SAVE_DATA_TYPE_CREDIT_CARD:
+                case SAVE_DATA_TYPE_USERNAME:
+                case SAVE_DATA_TYPE_EMAIL_ADDRESS:
                     mType = type;
                     break;
                 default:
@@ -269,26 +280,6 @@
             return this;
         }
 
-
-        /**
-         * @hide
-         */
-        // TODO(b/33197203): temporary fix to runtime crash
-        public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) {
-            throwIfDestroyed();
-
-            if (ids == null || ids.length == 0) {
-                return this;
-            }
-            if (mRequiredIds == null) {
-                mRequiredIds = new AutofillId[ids.length];
-            }
-            for (int i = 0; i < ids.length; i++) {
-                mRequiredIds[i] = ids[i].getDaRealId();
-            }
-            return this;
-        }
-
         /**
          * Sets an optional description to be shown in the UI when the user is asked to save.
          *
diff --git a/core/java/android/service/resolver/IResolverRankerResult.aidl b/core/java/android/service/resolver/IResolverRankerResult.aidl
deleted file mode 100644
index bda3154..0000000
--- a/core/java/android/service/resolver/IResolverRankerResult.aidl
+++ /dev/null
@@ -1,27 +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.service.resolver;
-
-import android.service.resolver.ResolverTarget;
-
-/**
- * @hide
- */
-oneway interface IResolverRankerResult
-{
-    void sendResult(in List<ResolverTarget> results);
-}
\ No newline at end of file
diff --git a/core/java/android/service/resolver/IResolverRankerService.aidl b/core/java/android/service/resolver/IResolverRankerService.aidl
deleted file mode 100644
index f0d747d..0000000
--- a/core/java/android/service/resolver/IResolverRankerService.aidl
+++ /dev/null
@@ -1,29 +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.service.resolver;
-
-import android.service.resolver.IResolverRankerResult;
-import android.service.resolver.ResolverTarget;
-
-/**
- * @hide
- */
-oneway interface IResolverRankerService
-{
-    void predict(in List<ResolverTarget> targets, IResolverRankerResult result);
-    void train(in List<ResolverTarget> targets, int selectedPosition);
-}
\ No newline at end of file
diff --git a/core/java/android/service/resolver/ResolverRankerService.java b/core/java/android/service/resolver/ResolverRankerService.java
deleted file mode 100644
index 0506747..0000000
--- a/core/java/android/service/resolver/ResolverRankerService.java
+++ /dev/null
@@ -1,187 +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.service.resolver;
-
-import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.RemoteException;
-import android.service.resolver.ResolverTarget;
-import android.util.Log;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * A service to rank apps according to usage stats of apps, when the system is resolving targets for
- * an Intent.
- *
- * <p>To extend this class, you must declare the service in your manifest file with the
- * {@link android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE} permission, and include an
- * intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
- * <pre>
- *     &lt;service android:name=".MyResolverRankerService"
- *             android:exported="true"
- *             android:priority="100"
- *             android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"&gt;
- *         &lt;intent-filter&gt;
- *             &lt;action android:name="android.service.resolver.ResolverRankerService" /&gt;
- *         &lt;/intent-filter&gt;
- *     &lt;/service&gt;
- * </pre>
- * @hide
- */
-@SystemApi
-public abstract class ResolverRankerService extends Service {
-
-    private static final String TAG = "ResolverRankerService";
-
-    private static final boolean DEBUG = false;
-
-    /**
-     * The Intent action that a service must respond to. Add it to the intent filter of the service
-     * in its manifest.
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE = "android.service.resolver.ResolverRankerService";
-
-    /**
-     * The permission that a service must require to ensure that only Android system can bind to it.
-     * If this permission is not enforced in the AndroidManifest of the service, the system will
-     * skip that service.
-     */
-    public static final String BIND_PERMISSION = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
-
-    private ResolverRankerServiceWrapper mWrapper = null;
-
-    /**
-     * Called by the system to retrieve a list of probabilities to rank apps/options. To implement
-     * it, set selectProbability of each input {@link ResolverTarget}. The higher the
-     * selectProbability is, the more likely the {@link ResolverTarget} will be selected by the
-     * user. Override this function to provide prediction results.
-     *
-     * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked.
-     *
-     * @throws Exception when the prediction task fails.
-     */
-    public void onPredictSharingProbabilities(final List<ResolverTarget> targets) {}
-
-    /**
-     * Called by the system to train/update a ranking service, after the user makes a selection from
-     * the ranked list of apps. Override this function to enable model updates.
-     *
-     * @param targets a list of {@link ResolverTarget}, for the list of apps to be ranked.
-     * @param selectedPosition the position of the selected app in the list.
-     *
-     * @throws Exception when the training task fails.
-     */
-    public void onTrainRankingModel(
-            final List<ResolverTarget> targets, final int selectedPosition) {}
-
-    private static final String HANDLER_THREAD_NAME = "RESOLVER_RANKER_SERVICE";
-    private volatile Handler mHandler;
-    private HandlerThread mHandlerThread;
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (DEBUG) Log.d(TAG, "onBind " + intent);
-        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
-            if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null");
-            return null;
-        }
-        if (mHandlerThread == null) {
-            mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME);
-            mHandlerThread.start();
-            mHandler = new Handler(mHandlerThread.getLooper());
-        }
-        if (mWrapper == null) {
-            mWrapper = new ResolverRankerServiceWrapper();
-        }
-        return mWrapper;
-    }
-
-    @Override
-    public void onDestroy() {
-        mHandler = null;
-        if (mHandlerThread != null) {
-            mHandlerThread.quitSafely();
-        }
-        super.onDestroy();
-    }
-
-    private static void sendResult(List<ResolverTarget> targets, IResolverRankerResult result) {
-        try {
-            result.sendResult(targets);
-        } catch (Exception e) {
-            Log.e(TAG, "failed to send results: " + e);
-        }
-    }
-
-    private class ResolverRankerServiceWrapper extends IResolverRankerService.Stub {
-
-        @Override
-        public void predict(final List<ResolverTarget> targets, final IResolverRankerResult result)
-                throws RemoteException {
-            Runnable predictRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "predict calls onPredictSharingProbabilities.");
-                        }
-                        onPredictSharingProbabilities(targets);
-                        sendResult(targets, result);
-                    } catch (Exception e) {
-                        Log.e(TAG, "onPredictSharingProbabilities failed; send null results: " + e);
-                        sendResult(null, result);
-                    }
-                }
-            };
-            final Handler h = mHandler;
-            if (h != null) {
-                h.post(predictRunnable);
-            }
-        }
-
-        @Override
-        public void train(final List<ResolverTarget> targets, final int selectedPosition)
-                throws RemoteException {
-            Runnable trainRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        if (DEBUG) {
-                            Log.d(TAG, "train calls onTranRankingModel");
-                        }
-                        onTrainRankingModel(targets, selectedPosition);
-                    } catch (Exception e) {
-                        Log.e(TAG, "onTrainRankingModel failed; skip train: " + e);
-                    }
-                }
-            };
-            final Handler h = mHandler;
-            if (h != null) {
-                h.post(trainRunnable);
-            }
-        }
-    }
-}
diff --git a/core/java/android/service/resolver/ResolverTarget.java b/core/java/android/service/resolver/ResolverTarget.java
deleted file mode 100644
index fb3e2d7..0000000
--- a/core/java/android/service/resolver/ResolverTarget.java
+++ /dev/null
@@ -1,216 +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.service.resolver;
-
-import android.annotation.SystemApi;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-
-import java.util.Map;
-
-/**
- * A ResolverTarget contains features by which an app or option will be ranked, in
- * {@link ResolverRankerService}.
- * @hide
- */
-@SystemApi
-public final class ResolverTarget implements Parcelable {
-    private static final String TAG = "ResolverTarget";
-
-    /**
-     * a float score for recency of last use.
-     */
-    private float mRecencyScore;
-
-    /**
-     * a float score for total time spent.
-     */
-    private float mTimeSpentScore;
-
-    /**
-     * a float score for number of launches.
-     */
-    private float mLaunchScore;
-
-    /**
-     * a float score for number of selected.
-     */
-    private float mChooserScore;
-
-    /**
-     * a float score for the probability to be selected.
-     */
-    private float mSelectProbability;
-
-    // constructor for the class.
-    public ResolverTarget() {}
-
-    ResolverTarget(Parcel in) {
-        mRecencyScore = in.readFloat();
-        mTimeSpentScore = in.readFloat();
-        mLaunchScore = in.readFloat();
-        mChooserScore = in.readFloat();
-        mSelectProbability = in.readFloat();
-    }
-
-    /**
-     * Gets the score for how recently the target was used in the foreground.
-     *
-     * @return a float score whose range is [0, 1]. The higher the score is, the more recently the
-     * target was used.
-     */
-    public float getRecencyScore() {
-        return mRecencyScore;
-    }
-
-    /**
-     * Sets the score for how recently the target was used in the foreground.
-     *
-     * @param recencyScore a float score whose range is [0, 1]. The higher the score is, the more
-     *                     recently the target was used.
-     */
-    public void setRecencyScore(float recencyScore) {
-        this.mRecencyScore = recencyScore;
-    }
-
-    /**
-     * Gets the score for how long the target has been used in the foreground.
-     *
-     * @return a float score whose range is [0, 1]. The higher the score is, the longer the target
-     * has been used for.
-     */
-    public float getTimeSpentScore() {
-        return mTimeSpentScore;
-    }
-
-    /**
-     * Sets the score for how long the target has been used in the foreground.
-     *
-     * @param timeSpentScore a float score whose range is [0, 1]. The higher the score is, the
-     *                       longer the target has been used for.
-     */
-    public void setTimeSpentScore(float timeSpentScore) {
-        this.mTimeSpentScore = timeSpentScore;
-    }
-
-    /**
-     * Gets the score for how many times the target has been launched to the foreground.
-     *
-     * @return a float score whose range is [0, 1]. The higher the score is, the more times the
-     * target has been launched.
-     */
-    public float getLaunchScore() {
-        return mLaunchScore;
-    }
-
-    /**
-     * Sets the score for how many times the target has been launched to the foreground.
-     *
-     * @param launchScore a float score whose range is [0, 1]. The higher the score is, the more
-     *                    times the target has been launched.
-     */
-    public void setLaunchScore(float launchScore) {
-        this.mLaunchScore = launchScore;
-    }
-
-    /**
-     * Gets the score for how many times the target has been selected by the user to share the same
-     * types of content.
-     *
-     * @return a float score whose range is [0, 1]. The higher the score is, the
-     * more times the target has been selected by the user to share the same types of content for.
-     */
-    public float getChooserScore() {
-        return mChooserScore;
-    }
-
-    /**
-     * Sets the score for how many times the target has been selected by the user to share the same
-     * types of content.
-     *
-     * @param chooserScore a float score whose range is [0, 1]. The higher the score is, the more
-     *                     times the target has been selected by the user to share the same types
-     *                     of content for.
-     */
-    public void setChooserScore(float chooserScore) {
-        this.mChooserScore = chooserScore;
-    }
-
-    /**
-     * Gets the probability of how likely this target will be selected by the user.
-     *
-     * @return a float score whose range is [0, 1]. The higher the score is, the more likely the
-     * user is going to select this target.
-     */
-    public float getSelectProbability() {
-        return mSelectProbability;
-    }
-
-    /**
-     * Sets the probability for how like this target will be selected by the user.
-     *
-     * @param selectProbability a float score whose range is [0, 1]. The higher the score is, the
-     *                          more likely tht user is going to select this target.
-     */
-    public void setSelectProbability(float selectProbability) {
-        this.mSelectProbability = selectProbability;
-    }
-
-    // serialize the class to a string.
-    @Override
-    public String toString() {
-        return "ResolverTarget{"
-                + mRecencyScore + ", "
-                + mTimeSpentScore + ", "
-                + mLaunchScore + ", "
-                + mChooserScore + ", "
-                + mSelectProbability + "}";
-    }
-
-    // describes the kinds of special objects contained in this Parcelable instance's marshaled
-    // representation.
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    // flattens this object in to a Parcel.
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeFloat(mRecencyScore);
-        dest.writeFloat(mTimeSpentScore);
-        dest.writeFloat(mLaunchScore);
-        dest.writeFloat(mChooserScore);
-        dest.writeFloat(mSelectProbability);
-    }
-
-    // creator definition for the class.
-    public static final Creator<ResolverTarget> CREATOR
-            = new Creator<ResolverTarget>() {
-        @Override
-        public ResolverTarget createFromParcel(Parcel source) {
-            return new ResolverTarget(source);
-        }
-
-        @Override
-        public ResolverTarget[] newArray(int size) {
-            return new ResolverTarget[size];
-        }
-    };
-}
diff --git a/core/java/android/text/method/DateKeyListener.java b/core/java/android/text/method/DateKeyListener.java
index e14cd2c..0accbf6 100644
--- a/core/java/android/text/method/DateKeyListener.java
+++ b/core/java/android/text/method/DateKeyListener.java
@@ -22,6 +22,7 @@
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.HashMap;
 import java.util.LinkedHashSet;
@@ -37,8 +38,11 @@
 public class DateKeyListener extends NumberKeyListener
 {
     public int getInputType() {
-        return InputType.TYPE_CLASS_DATETIME
-                | InputType.TYPE_DATETIME_VARIATION_DATE;
+        if (mNeedsAdvancedInput) {
+            return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+        } else {
+            return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE;
+        }
     }
 
     @Override
@@ -65,7 +69,13 @@
         final boolean success = NumberKeyListener.addDigits(chars, locale)
                                 && NumberKeyListener.addFormatCharsFromSkeletons(
                                         chars, locale, SKELETONS, SYMBOLS_TO_IGNORE);
-        mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS;
+        if (success) {
+            mCharacters = NumberKeyListener.collectionToArray(chars);
+            mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters);
+        } else {
+            mCharacters = CHARACTERS;
+            mNeedsAdvancedInput = false;
+        }
     }
 
     /**
@@ -110,6 +120,7 @@
         };
 
     private final char[] mCharacters;
+    private final boolean mNeedsAdvancedInput;
 
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
diff --git a/core/java/android/text/method/DateTimeKeyListener.java b/core/java/android/text/method/DateTimeKeyListener.java
index 62e3ade..551db55 100644
--- a/core/java/android/text/method/DateTimeKeyListener.java
+++ b/core/java/android/text/method/DateTimeKeyListener.java
@@ -22,6 +22,7 @@
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.HashMap;
 import java.util.LinkedHashSet;
@@ -37,10 +38,13 @@
 public class DateTimeKeyListener extends NumberKeyListener
 {
     public int getInputType() {
-        return InputType.TYPE_CLASS_DATETIME
-                | InputType.TYPE_DATETIME_VARIATION_NORMAL;
+        if (mNeedsAdvancedInput) {
+            return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+        } else {
+            return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_NORMAL;
+        }
     }
-    
+
     @Override
     @NonNull
     protected char[] getAcceptedChars()
@@ -70,7 +74,13 @@
                               chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE)
                           && NumberKeyListener.addFormatCharsFromSkeleton(
                               chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE);
-        mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS;
+        if (success) {
+            mCharacters = NumberKeyListener.collectionToArray(chars);
+            mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters);
+        } else {
+            mCharacters = CHARACTERS;
+            mNeedsAdvancedInput = false;
+        }
     }
 
     /**
@@ -114,6 +124,7 @@
         };
 
     private final char[] mCharacters;
+    private final boolean mNeedsAdvancedInput;
 
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java
index 26c69ab..d9f2dcf 100644
--- a/core/java/android/text/method/DigitsKeyListener.java
+++ b/core/java/android/text/method/DigitsKeyListener.java
@@ -27,6 +27,7 @@
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.HashMap;
 import java.util.LinkedHashSet;
@@ -42,8 +43,12 @@
 public class DigitsKeyListener extends NumberKeyListener
 {
     private char[] mAccepted;
+    private boolean mNeedsAdvancedInput;
     private final boolean mSign;
     private final boolean mDecimal;
+    private final boolean mStringMode;
+    @Nullable
+    private final Locale mLocale;
 
     private static final String DEFAULT_DECIMAL_POINT_CHARS = ".";
     private static final String DEFAULT_SIGN_CHARS = "-+";
@@ -112,11 +117,17 @@
         this(locale, false, false);
     }
 
-    private void setToCompat(boolean sign, boolean decimal) {
+    private void setToCompat() {
         mDecimalPointChars = DEFAULT_DECIMAL_POINT_CHARS;
         mSignChars = DEFAULT_SIGN_CHARS;
-        final int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0);
+        final int kind = (mSign ? SIGN : 0) | (mDecimal ? DECIMAL : 0);
         mAccepted = COMPATIBILITY_CHARACTERS[kind];
+        mNeedsAdvancedInput = false;
+    }
+
+    private void calculateNeedForAdvancedInput() {
+        final int kind = (mSign ? SIGN : 0) | (mDecimal ? DECIMAL : 0);
+        mNeedsAdvancedInput = !ArrayUtils.containsAll(COMPATIBILITY_CHARACTERS[kind], mAccepted);
     }
 
     // Takes a sign string and strips off its bidi controls, if any.
@@ -144,14 +155,16 @@
     public DigitsKeyListener(@Nullable Locale locale, boolean sign, boolean decimal) {
         mSign = sign;
         mDecimal = decimal;
+        mStringMode = false;
+        mLocale = locale;
         if (locale == null) {
-            setToCompat(sign, decimal);
+            setToCompat();
             return;
         }
         LinkedHashSet<Character> chars = new LinkedHashSet<>();
         final boolean success = NumberKeyListener.addDigits(chars, locale);
         if (!success) {
-            setToCompat(sign, decimal);
+            setToCompat();
             return;
         }
         if (sign || decimal) {
@@ -161,7 +174,7 @@
                 final String plusString = stripBidiControls(symbols.getPlusSignString());
                 if (minusString.length() > 1 || plusString.length() > 1) {
                     // non-BMP and multi-character signs are not supported.
-                    setToCompat(sign, decimal);
+                    setToCompat();
                     return;
                 }
                 final char minus = minusString.charAt(0);
@@ -181,7 +194,7 @@
                 final String separatorString = symbols.getDecimalSeparatorString();
                 if (separatorString.length() > 1) {
                     // non-BMP and multi-character decimal separators are not supported.
-                    setToCompat(sign, decimal);
+                    setToCompat();
                     return;
                 }
                 final Character separatorChar = Character.valueOf(separatorString.charAt(0));
@@ -190,13 +203,19 @@
             }
         }
         mAccepted = NumberKeyListener.collectionToArray(chars);
+        calculateNeedForAdvancedInput();
     }
 
     private DigitsKeyListener(@NonNull final String accepted) {
         mSign = false;
         mDecimal = false;
+        mStringMode = true;
+        mLocale = null;
         mAccepted = new char[accepted.length()];
         accepted.getChars(0, accepted.length(), mAccepted, 0);
+        // Theoretically we may need advanced input, but for backward compatibility, we don't change
+        // the input type.
+        mNeedsAdvancedInput = false;
     }
 
     /**
@@ -280,13 +299,38 @@
         return result;
     }
 
-    public int getInputType() {
-        int contentType = InputType.TYPE_CLASS_NUMBER;
-        if (mSign) {
-            contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
+    /**
+     * Returns a DigitsKeyListener based on an the settings of a existing DigitsKeyListener, with
+     * the locale modified.
+     *
+     * @hide
+     */
+    @NonNull
+    public static DigitsKeyListener getInstance(
+            @Nullable Locale locale,
+            @NonNull DigitsKeyListener listener) {
+        if (listener.mStringMode) {
+            return listener; // string-mode DigitsKeyListeners have no locale.
+        } else {
+            return getInstance(locale, listener.mSign, listener.mDecimal);
         }
-        if (mDecimal) {
-            contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
+    }
+
+    /**
+     * Returns the input type for the listener.
+     */
+    public int getInputType() {
+        int contentType;
+        if (mNeedsAdvancedInput) {
+            contentType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+        } else {
+            contentType = InputType.TYPE_CLASS_NUMBER;
+            if (mSign) {
+                contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
+            }
+            if (mDecimal) {
+                contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
+            }
         }
         return contentType;
     }
diff --git a/core/java/android/text/method/TimeKeyListener.java b/core/java/android/text/method/TimeKeyListener.java
index c9f9f9f..5b1db11 100644
--- a/core/java/android/text/method/TimeKeyListener.java
+++ b/core/java/android/text/method/TimeKeyListener.java
@@ -22,6 +22,7 @@
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.HashMap;
 import java.util.LinkedHashSet;
@@ -37,8 +38,11 @@
 public class TimeKeyListener extends NumberKeyListener
 {
     public int getInputType() {
-        return InputType.TYPE_CLASS_DATETIME
-        | InputType.TYPE_DATETIME_VARIATION_TIME;
+        if (mNeedsAdvancedInput) {
+            return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+        } else {
+            return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME;
+        }
     }
 
     @Override
@@ -70,7 +74,13 @@
                               chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE)
                           && NumberKeyListener.addFormatCharsFromSkeleton(
                               chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE);
-        mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS;
+        if (success) {
+            mCharacters = NumberKeyListener.collectionToArray(chars);
+            mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters);
+        } else {
+            mCharacters = CHARACTERS;
+            mNeedsAdvancedInput = false;
+        }
     }
 
     /**
@@ -114,6 +124,7 @@
         };
 
     private final char[] mCharacters;
+    private final boolean mNeedsAdvancedInput;
 
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0221040..a376c83 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2750,7 +2750,7 @@
      *                    1              PFLAG3_SCROLL_INDICATOR_END
      *                   1               PFLAG3_ASSIST_BLOCKED
      *                  1                PFLAG3_CLUSTER
-     *                 x                 * NO LONGER NEEDED, SHOULD BE REUSED *
+     *                 1                 PFLAG3_IS_AUTOFILLED
      *                1                  PFLAG3_FINGER_DOWN
      *               1                   PFLAG3_FOCUSED_BY_DEFAULT
      *             11                    PFLAG3_AUTO_FILL_MODE_MASK
@@ -2961,6 +2961,14 @@
     private static final int PFLAG3_CLUSTER = 0x8000;
 
     /**
+     * Flag indicating that the view is autofilled
+     *
+     * @see #isAutofilled()
+     * @see #setAutofilled(boolean)
+     */
+    private static final int PFLAG3_IS_AUTOFILLED = 0x10000;
+
+    /**
      * Indicates that the user is currently touching the screen.
      * Currently used for the tooltip positioning only.
      */
@@ -7440,10 +7448,9 @@
      * <ol>
      * <li>Calling the proper getter method on {@link AutofillValue} to fetch the actual value.
      * <li>Passing the actual value to the equivalent setter in the view.
-     * <ol>
+     * </ol>
      *
      * <p>For example, a text-field view would call:
-     *
      * <pre class="prettyprint">
      * CharSequence text = value.getTextValue();
      * if (text != null) {
@@ -7451,6 +7458,10 @@
      * }
      * </pre>
      *
+     * <p>If the value is updated asyncronously the next call to
+     * {@link AutofillManager#notifyValueChanged(View)} must happen <u>after</u> the value was
+     * changed to the autofilled value. If not, the view will not be considered autofilled.
+     *
      * @param value value to be autofilled.
      */
     public void autofill(@SuppressWarnings("unused") AutofillValue value) {
@@ -7461,6 +7472,9 @@
      *
      * <p>See {@link #autofill(AutofillValue)} and
      * {@link #onProvideAutofillVirtualStructure(ViewStructure, int)} for more info.
+     * <p>To indicate that a virtual view was autofilled
+     * <code>@android:drawable/autofilled_highlight</code> should be drawn over it until the data
+     * changes.
      *
      * @param values map of values to be autofilled, keyed by virtual child id.
      */
@@ -7491,6 +7505,13 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean isAutofilled() {
+        return (mPrivateFlags3 & PFLAG3_IS_AUTOFILLED) != 0;
+    }
+
+    /**
      * Gets the {@link View}'s current autofill value.
      *
      * <p>By default returns {@code null}, but views should override it (and
@@ -9131,6 +9152,24 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public void setAutofilled(boolean isAutofilled) {
+        boolean wasChanged = isAutofilled != isAutofilled();
+
+        if (wasChanged) {
+            if (isAutofilled) {
+                mPrivateFlags3 |= PFLAG3_IS_AUTOFILLED;
+            } else {
+                mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED;
+            }
+
+            invalidate();
+        }
+    }
+
+    /**
      * Set whether this view should have sound effects enabled for events such as
      * clicking and touching.
      *
@@ -17117,9 +17156,19 @@
     @CallSuper
     protected Parcelable onSaveInstanceState() {
         mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
-        if (mStartActivityRequestWho != null) {
+        if (mStartActivityRequestWho != null || isAutofilled()) {
             BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
+
+            if (mStartActivityRequestWho != null) {
+                state.mSavedData |= BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED;
+            }
+
+            if (isAutofilled()) {
+                state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
+            }
+
             state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
+            state.mIsAutofilled = isAutofilled();
             return state;
         }
         return BaseSavedState.EMPTY_STATE;
@@ -17189,7 +17238,14 @@
                     + "other views do not use the same id.");
         }
         if (state != null && state instanceof BaseSavedState) {
-            mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
+            BaseSavedState baseState = (BaseSavedState) state;
+
+            if ((baseState.mSavedData & BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED) != 0) {
+                mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved;
+            }
+            if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
+                setAutofilled(baseState.mIsAutofilled);
+            }
         }
     }
 
@@ -17570,6 +17626,7 @@
                     // Fast path for layouts with no backgrounds
                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                         dispatchDraw(canvas);
+                        drawAutofilledHighlight(canvas);
                         if (mOverlay != null && !mOverlay.isEmpty()) {
                             mOverlay.getOverlayView().draw(canvas);
                         }
@@ -17870,6 +17927,7 @@
         if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
             mPrivateFlags &= ~PFLAG_DIRTY_MASK;
             dispatchDraw(canvas);
+            drawAutofilledHighlight(canvas);
             if (mOverlay != null && !mOverlay.isEmpty()) {
                 mOverlay.getOverlayView().draw(canvas);
             }
@@ -17946,6 +18004,7 @@
         // Fast path for layouts with no backgrounds
         if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
             dispatchDraw(canvas);
+            drawAutofilledHighlight(canvas);
             if (mOverlay != null && !mOverlay.isEmpty()) {
                 mOverlay.getOverlayView().draw(canvas);
             }
@@ -18630,6 +18689,8 @@
             // Step 4, draw the children
             dispatchDraw(canvas);
 
+            drawAutofilledHighlight(canvas);
+
             // Overlay is part of the content and draws beneath Foreground
             if (mOverlay != null && !mOverlay.isEmpty()) {
                 mOverlay.getOverlayView().dispatchDraw(canvas);
@@ -18783,6 +18844,8 @@
 
         canvas.restoreToCount(saveCount);
 
+        drawAutofilledHighlight(canvas);
+
         // Overlay is part of the content and draws beneath Foreground
         if (mOverlay != null && !mOverlay.isEmpty()) {
             mOverlay.getOverlayView().dispatchDraw(canvas);
@@ -20141,6 +20204,41 @@
     }
 
     /**
+     * Get the drawable to be overlayed when a view is autofilled
+     *
+     * @return The drawable
+     *
+     * @throws IllegalStateException if the drawable could not be found.
+     */
+    @NonNull private Drawable getAutofilledDrawable() {
+        // Lazily load the isAutofilled drawable.
+        if (mAttachInfo.mAutofilledDrawable == null) {
+            mAttachInfo.mAutofilledDrawable = mContext.getDrawable(R.drawable.autofilled_highlight);
+
+            if (mAttachInfo.mAutofilledDrawable == null) {
+                throw new IllegalStateException(
+                        "Could not find android:drawable/autofilled_highlight");
+            }
+        }
+
+        return mAttachInfo.mAutofilledDrawable;
+    }
+
+    /**
+     * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled.
+     *
+     * @param canvas The canvas to draw on
+     */
+    private void drawAutofilledHighlight(@NonNull Canvas canvas) {
+        if (isAutofilled()) {
+            Drawable autofilledHighlight = getAutofilledDrawable();
+
+            autofilledHighlight.setBounds(0, 0, getWidth(), getHeight());
+            autofilledHighlight.draw(canvas);
+        }
+    }
+
+    /**
      * Draw any foreground content for this view.
      *
      * <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground}
@@ -20808,11 +20906,18 @@
     }
 
     /**
-     * Look for a child view with the given id.  If this view has the given
-     * id, return this view.
+     * Finds the first descendant view with the given ID, the view itself if
+     * the ID matches {@link #getId()}, or {@code null} if the ID is invalid
+     * (< 0) or there is no matching view in the hierarchy.
+     * <p>
+     * <strong>Note:</strong> In most cases -- depending on compiler support --
+     * the resulting view is automatically cast to the target class type. If
+     * the target class type is unconstrained, an explicit cast may be
+     * necessary.
      *
-     * @param id The id to search for.
-     * @return The view that has the given id in the hierarchy or null
+     * @param id the ID to search for
+     * @return a view with given ID if found, or {@code null} otherwise
+     * @see View#findViewById(int)
      */
     @Nullable
     public final <T extends View> T findViewById(@IdRes int id) {
@@ -24298,7 +24403,13 @@
      * state in {@link android.view.View#onSaveInstanceState()}.
      */
     public static class BaseSavedState extends AbsSavedState {
+        static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1;
+        static final int IS_AUTOFILLED = 0b10;
+
+        // Flags that describe what data in this state is valid
+        int mSavedData;
         String mStartActivityRequestWhoSaved;
+        boolean mIsAutofilled;
 
         /**
          * Constructor used when reading from a parcel. Reads the state of the superclass.
@@ -24318,7 +24429,9 @@
          */
         public BaseSavedState(Parcel source, ClassLoader loader) {
             super(source, loader);
+            mSavedData = source.readInt();
             mStartActivityRequestWhoSaved = source.readString();
+            mIsAutofilled = source.readBoolean();
         }
 
         /**
@@ -24333,7 +24446,10 @@
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
+
+            out.writeInt(mSavedData);
             out.writeString(mStartActivityRequestWhoSaved);
+            out.writeBoolean(mIsAutofilled);
         }
 
         public static final Parcelable.Creator<BaseSavedState> CREATOR
@@ -24734,6 +24850,13 @@
         Drawable mAccessibilityFocusDrawable;
 
         /**
+         * The drawable for highlighting autofilled views.
+         *
+         * @see #isAutofilled()
+         */
+        Drawable mAutofilledDrawable;
+
+        /**
          * Show where the margins, bounds and layout bounds are for each view.
          */
         boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0053caa..6dd8ecf 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1308,15 +1308,22 @@
     }
 
     /**
-     * Finds a view that was identified by the id attribute from the XML that
-     * was processed in {@link android.app.Activity#onCreate}.  This will
-     * implicitly call {@link #getDecorView} for you, with all of the
-     * associated side-effects.
+     * Finds a view that was identified by the {@code android:id} XML attribute
+     * that was processed in {@link android.app.Activity#onCreate}. This will
+     * implicitly call {@link #getDecorView} with all of the associated
+     * side-effects.
+     * <p>
+     * <strong>Note:</strong> In most cases -- depending on compiler support --
+     * the resulting view is automatically cast to the target class type. If
+     * the target class type is unconstrained, an explicit cast may be
+     * necessary.
      *
-     * @return The view if found or null otherwise.
+     * @param id the ID to search for
+     * @return a view with given ID if found, or {@code null} otherwise
+     * @see View#findViewById(int)
      */
     @Nullable
-    public View findViewById(@IdRes int id) {
+    public <T extends View> T findViewById(@IdRes int id) {
         return getDecorView().findViewById(id);
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 35276cc..5e6ace7 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -41,6 +41,7 @@
 import android.view.IWindow;
 import android.view.View;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IntPair;
 
 import java.util.ArrayList;
@@ -126,6 +127,8 @@
 
     final Handler mHandler;
 
+    final Handler.Callback mCallback;
+
     boolean mIsEnabled;
 
     int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
@@ -217,12 +220,12 @@
             // is now off an exception will be thrown. We want to have the exception
             // enforcement to guard against apps that fire unnecessary accessibility
             // events when accessibility is off.
-            mHandler.obtainMessage(MyHandler.MSG_SET_STATE, state, 0).sendToTarget();
+            mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget();
         }
 
         @Override
         public void notifyServicesStateChanged() {
-            mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
+            mHandler.obtainMessage(MyCallback.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
         }
 
         @Override
@@ -271,7 +274,8 @@
     public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
         // Constructor can't be chained because we can't create an instance of an inner class
         // before calling another constructor.
-        mHandler = new MyHandler(context.getMainLooper());
+        mCallback = new MyCallback();
+        mHandler = new Handler(context.getMainLooper(), mCallback);
         mUserId = userId;
         synchronized (mLock) {
             tryConnectToServiceLocked(service);
@@ -288,6 +292,7 @@
      * @hide
      */
     public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+        mCallback = new MyCallback();
         mHandler = handler;
         mUserId = userId;
         synchronized (mLock) {
@@ -303,6 +308,14 @@
     }
 
     /**
+     * @hide
+     */
+    @VisibleForTesting
+    public Handler.Callback getCallback() {
+        return mCallback;
+    }
+
+    /**
      * Returns if the accessibility in the system is enabled.
      *
      * @return True if accessibility is enabled, false otherwise.
@@ -711,15 +724,15 @@
         mIsHighTextContrastEnabled = highTextContrastEnabled;
 
         if (wasEnabled != enabled) {
-            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
+            mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
         }
 
         if (wasTouchExplorationEnabled != touchExplorationEnabled) {
-            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
+            mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
         }
 
         if (wasHighTextContrastEnabled != highTextContrastEnabled) {
-            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED);
+            mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED);
         }
     }
 
@@ -960,19 +973,15 @@
         }
     }
 
-    private final class MyHandler extends Handler {
+    private final class MyCallback implements Handler.Callback {
         public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
         public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
         public static final int MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED = 3;
         public static final int MSG_SET_STATE = 4;
         public static final int MSG_NOTIFY_SERVICES_STATE_CHANGED = 5;
 
-        public MyHandler(Looper looper) {
-            super(looper, null, false);
-        }
-
         @Override
-        public void handleMessage(Message message) {
+        public boolean handleMessage(Message message) {
             switch (message.what) {
                 case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: {
                     handleNotifyAccessibilityStateChanged();
@@ -998,6 +1007,7 @@
                     }
                 } break;
             }
+            return true;
         }
     }
 }
diff --git a/core/java/android/view/autofill/AutoFillId.aidl b/core/java/android/view/autofill/AutoFillId.aidl
deleted file mode 100644
index fc57ce7..0000000
--- a/core/java/android/view/autofill/AutoFillId.aidl
+++ /dev/null
@@ -1,20 +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;
-
-//  @deprecated TODO(b/35956626): remove once clients use AutofillId
-parcelable AutoFillId;
\ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java
deleted file mode 100644
index 081fb02..0000000
--- a/core/java/android/view/autofill/AutoFillId.java
+++ /dev/null
@@ -1,85 +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;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
- */
-@Deprecated
-public final class AutoFillId implements Parcelable {
-
-    private final AutofillId mRealId;
-
-    /** @hide */
-    public AutoFillId(AutofillId daRealId) {
-        this.mRealId = daRealId;
-    }
-
-    @Override
-    public int hashCode() {
-        return mRealId.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        final AutoFillId other = (AutoFillId) obj;
-        return mRealId.equals(other.mRealId);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mRealId, 0);
-    }
-
-    private AutoFillId(Parcel parcel) {
-        mRealId = parcel.readParcelable(null);
-    }
-
-    /** @hide */
-    public AutofillId getDaRealId() {
-        return mRealId;
-    }
-
-    /** @hide */
-    public static AutoFillId forDaRealId(AutofillId id) {
-        return id == null ? null : new AutoFillId(id);
-    }
-
-    public static final Parcelable.Creator<AutoFillId> CREATOR =
-            new Parcelable.Creator<AutoFillId>() {
-        @Override
-        public AutoFillId createFromParcel(Parcel source) {
-            return new AutoFillId(source);
-        }
-
-        @Override
-        public AutoFillId[] newArray(int size) {
-            return new AutoFillId[size];
-        }
-    };
-}
diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java
deleted file mode 100644
index c508ba4..0000000
--- a/core/java/android/view/autofill/AutoFillType.java
+++ /dev/null
@@ -1,188 +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;
-
-import static android.view.autofill.Helper.DEBUG;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-/**
- * Defines the type of a object that can be used to autofill a {@link View} so the
- * {@link android.service.autofill.AutofillService} can use the proper {@link AutofillValue} to
- * fill it.
- *
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
- */
-@Deprecated
-public final class AutoFillType implements Parcelable {
-
-    // Cached instance for types that don't have subtype; it uses the "lazy initialization holder
-    // class idiom" (Effective Java, Item 71) to avoid memory utilization when autofill is not
-    // enabled.
-    private static class DefaultTypesHolder {
-        static final AutoFillType TEXT = new AutoFillType(TYPE_TEXT);
-        static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE);
-        static final AutoFillType LIST = new AutoFillType(TYPE_LIST);
-        static final AutoFillType DATE = new AutoFillType(TYPE_DATE);
-    }
-
-    private static final int TYPE_TEXT = 1;
-    private static final int TYPE_TOGGLE = 2;
-    private static final int TYPE_LIST = 3;
-    private static final int TYPE_DATE = 4;
-
-    private final int mType;
-
-    private AutoFillType(int type) {
-        mType = type;
-    }
-
-    /**
-     * Checks if this is a type for a text field, which is filled by a {@link CharSequence}.
-     */
-    public boolean isText() {
-        return mType == TYPE_TEXT;
-    }
-
-    /**
-     * Checks if this is a a type for a togglable field, which is filled by a {@code boolean}.
-     */
-    public boolean isToggle() {
-        return mType == TYPE_TOGGLE;
-    }
-
-    /**
-     * Checks if this is a type for a selection list field, which is filled by a {@code integer}
-     * representing the element index inside the list (starting at {@code 0}.
-      */
-    public boolean isList() {
-        return mType == TYPE_LIST;
-    }
-
-    /**
-     * Checks if this is a type for a date and time, which is represented by a long representing
-     * the number of milliseconds since the standard base time known as "the epoch", namely
-     * January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}.
-     */
-    public boolean isDate() {
-        return mType == TYPE_DATE;
-    }
-
-    /////////////////////////////////////
-    //  Object "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public String toString() {
-        if (!DEBUG) return super.toString();
-
-        return "AutoFillType [type=" + mType + "]";
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mType;
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        final AutoFillType other = (AutoFillType) obj;
-        if (mType != other.mType) return false;
-        return true;
-    }
-
-    /////////////////////////////////////
-    //  Parcelable "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeInt(mType);
-    }
-
-    private AutoFillType(Parcel parcel) {
-        mType = parcel.readInt();
-    }
-
-    public static final Parcelable.Creator<AutoFillType> CREATOR =
-            new Parcelable.Creator<AutoFillType>() {
-        @Override
-        public AutoFillType createFromParcel(Parcel source) {
-            return new AutoFillType(source);
-        }
-
-        @Override
-        public AutoFillType[] newArray(int size) {
-            return new AutoFillType[size];
-        }
-    };
-
-    ////////////////////
-    // Factory methods //
-    ////////////////////
-
-    /**
-     * Creates a text field type, which is filled by a {@link CharSequence}.
-     *
-     * <p>See {@link #isText()} for more info.
-     */
-    public static AutoFillType forText() {
-        return DefaultTypesHolder.TEXT;
-    }
-
-    /**
-     * Creates a type that can be toggled which is filled by a {@code boolean}.
-     *
-     * <p>See {@link #isToggle()} for more info.
-     */
-    public static AutoFillType forToggle() {
-        return DefaultTypesHolder.TOGGLE;
-    }
-
-    /**
-     * Creates a selection list, which is filled by a {@code integer} representing the element index
-     * inside the list (starting at {@code 0}.
-     *
-     * <p>See {@link #isList()} for more info.
-     */
-    public static AutoFillType forList() {
-        return DefaultTypesHolder.LIST;
-    }
-
-    /**
-     * Creates a type that represents a date.
-     *
-     * <p>See {@link #isDate()} for more info.
-     */
-    public static AutoFillType forDate() {
-        return DefaultTypesHolder.DATE;
-    }
-}
diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
deleted file mode 100644
index 4774d8f..0000000
--- a/core/java/android/view/autofill/AutoFillValue.java
+++ /dev/null
@@ -1,181 +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.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-/**
- * @hide
- * @deprecated TODO(b/35956626): remove once clients use AutofillValue
- */
-@Deprecated
-public final class AutoFillValue implements Parcelable {
-    private final AutofillValue mRealValue;
-
-    private AutoFillValue(AutofillValue daRealValue) {
-        this.mRealValue = daRealValue;
-    }
-
-    /**
-     * Gets the value to autofill a text field.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
-     */
-    public CharSequence getTextValue() {
-        return mRealValue.getTextValue();
-    }
-
-    /**
-     * Gets the value to autofill a toggable field.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
-     */
-    public boolean getToggleValue() {
-        return mRealValue.getToggleValue();
-    }
-
-    /**
-     * Gets the value to autofill a selection list field.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
-     */
-    public int getListValue() {
-        return mRealValue.getListValue();
-    }
-
-    /**
-     * Gets the value to autofill a date field.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
-     */
-    public long getDateValue() {
-        return mRealValue.getDateValue();
-    }
-
-    /////////////////////////////////////
-    //  Object "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public int hashCode() {
-        return mRealValue.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        final AutoFillValue other = (AutoFillValue) obj;
-        return mRealValue.equals(other.mRealValue);
-    }
-
-    @Override
-    public String toString() {
-        if (!DEBUG) return super.toString();
-
-        return mRealValue.toString();
-    }
-
-    /////////////////////////////////////
-    //  Parcelable "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mRealValue, 0);
-    }
-
-    private AutoFillValue(Parcel parcel) {
-        mRealValue = parcel.readParcelable(null);
-    }
-
-    public static final Parcelable.Creator<AutoFillValue> CREATOR =
-            new Parcelable.Creator<AutoFillValue>() {
-        @Override
-        public AutoFillValue createFromParcel(Parcel source) {
-            return new AutoFillValue(source);
-        }
-
-        @Override
-        public AutoFillValue[] newArray(int size) {
-            return new AutoFillValue[size];
-        }
-    };
-
-    ////////////////////
-    // Factory methods //
-    ////////////////////
-    /**
-     * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a text field.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
-     */
-    @Nullable
-    public static AutoFillValue forText(@Nullable CharSequence value) {
-        return value == null ? null : new AutoFillValue(AutofillValue.forText(value));
-    }
-
-    /**
-     * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a toggable
-     * field.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
-     */
-    public static AutoFillValue forToggle(boolean value) {
-        return new AutoFillValue(AutofillValue.forToggle(value));
-    }
-
-    /**
-     * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a selection
-     * list.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
-     */
-    public static AutoFillValue forList(int value) {
-        return new AutoFillValue(AutofillValue.forList(value));
-    }
-
-    /**
-     * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a date.
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
-     */
-    public static AutoFillValue forDate(long date) {
-        return new AutoFillValue(AutofillValue.forDate(date));
-    }
-
-    /** @hide */
-    public static AutoFillValue forDaRealValue(AutofillValue daRealValue) {
-        return new AutoFillValue(daRealValue);
-    }
-
-    /** @hide */
-    public AutofillValue getDaRealValue() {
-        return mRealValue;
-    }
-}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 19980fb..07fad60 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -44,6 +44,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * App entry point to the AutoFill Framework.
@@ -91,6 +92,8 @@
      */
     public static final String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
 
+    static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
+
     // Public flags start from the lowest bit
     /**
      * Indicates autofill was explicitly requested by the user.
@@ -115,6 +118,9 @@
     private boolean mHasSession;
     private boolean mEnabled;
 
+    /** If a view changes to this mapping the autofill operation was successful */
+    @Nullable private ParcelableMap mLastAutofilledData;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -160,7 +166,31 @@
     }
 
     /**
-     * Checkes whether autofill is enabled for the current user.
+     * Restore state after activity lifecycle
+     *
+     * @param savedInstanceState The state to be restored
+     *
+     * {@hide}
+     */
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+        mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
+    }
+
+    /**
+     * Save state before activity lifecycle
+     *
+     * @param outState Place to store the state
+     *
+     * {@hide}
+     */
+    public void onSaveInstanceState(Bundle outState) {
+        if (mLastAutofilledData != null) {
+            outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
+        }
+    }
+
+    /**
+     * Checks whether autofill is enabled for the current user.
      *
      * <p>Typically used to determine whether the option to explicitly request autofill should
      * be offered - see {@link #requestAutofill(View)}.
@@ -311,12 +341,43 @@
      * @param view view whose value changed.
      */
     public void notifyValueChanged(View view) {
+        AutofillId id = null;
+        boolean valueWasRead = false;
+        AutofillValue value = null;
+
+        // If the session is gone some fields might still be highlighted, hence we have to remove
+        // the isAutofilled property even if no sessions are active.
+        if (mLastAutofilledData == null) {
+            view.setAutofilled(false);
+        } else {
+            id = getAutofillId(view);
+            if (mLastAutofilledData.containsKey(id)) {
+                value = view.getAutofillValue();
+                valueWasRead = true;
+
+                if (Objects.equals(mLastAutofilledData.get(id), value)) {
+                    view.setAutofilled(true);
+                } else {
+                    view.setAutofilled(false);
+                    mLastAutofilledData.remove(id);
+                }
+            } else {
+                view.setAutofilled(false);
+            }
+        }
+
         if (!mEnabled || !mHasSession) {
             return;
         }
 
-        final AutofillId id = getAutofillId(view);
-        final AutofillValue value = view.getAutofillValue();
+        if (id == null) {
+            id = getAutofillId(view);
+        }
+
+        if (!valueWasRead) {
+            value = view.getAutofillValue();
+        }
+
         updateSession(id, null, value, FLAG_VALUE_CHANGED);
     }
 
@@ -535,6 +596,23 @@
         }
     }
 
+    /**
+     * Sets a view as autofilled if the current value is the {code targetValue}.
+     *
+     * @param view The view that is to be autofilled
+     * @param targetValue The value we want to fill into view
+     */
+    private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
+        AutofillValue currentValue = view.getAutofillValue();
+        if (Objects.equals(currentValue, targetValue)) {
+            if (mLastAutofilledData == null) {
+                mLastAutofilledData = new ParcelableMap(1);
+            }
+            mLastAutofilledData.put(getAutofillId(view), targetValue);
+            view.setAutofilled(true);
+        }
+    }
+
     private void handleAutofill(IBinder windowToken, List<AutofillId> ids,
             List<AutofillValue> values) {
         final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
@@ -568,7 +646,19 @@
                 }
                 valuesByParent.put(id.getVirtualChildId(), value);
             } else {
+                // Mark the view as to be autofilled with 'value'
+                if (mLastAutofilledData == null) {
+                    mLastAutofilledData = new ParcelableMap(itemCount - i);
+                }
+                mLastAutofilledData.put(id, value);
+
                 view.autofill(value);
+
+                // Set as autofilled if the values match now, e.g. when the value was updated
+                // synchronously.
+                // If autofill happens async, the view is set to autofilled in notifyValueChanged.
+                setAutofilledIfValuesIs(view, value);
+
                 numApplied++;
             }
         }
diff --git a/core/java/android/view/autofill/ParcelableMap.java b/core/java/android/view/autofill/ParcelableMap.java
new file mode 100644
index 0000000..f97b1a0
--- /dev/null
+++ b/core/java/android/view/autofill/ParcelableMap.java
@@ -0,0 +1,73 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A parcelable HashMap for {@link AutofillId} and {@link AutofillValue}
+ *
+ * {@hide}
+ */
+class ParcelableMap extends HashMap<AutofillId, AutofillValue> implements Parcelable {
+    ParcelableMap(int size) {
+        super(size);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(size());
+
+        for (Map.Entry<AutofillId, AutofillValue> entry : entrySet()) {
+            dest.writeParcelable(entry.getKey(), 0);
+            dest.writeParcelable(entry.getValue(), 0);
+        }
+    }
+
+    public static final Parcelable.Creator<ParcelableMap> CREATOR =
+            new Parcelable.Creator<ParcelableMap>() {
+                @Override
+                public ParcelableMap createFromParcel(Parcel source) {
+                    int size = source.readInt();
+
+                    ParcelableMap map = new ParcelableMap(size);
+
+                    for (int i = 0; i < size; i++) {
+                        AutofillId key = source.readParcelable(null);
+                        AutofillValue value = source.readParcelable(null);
+
+                        map.put(key, value);
+                    }
+
+                    return map;
+                }
+
+                @Override
+                public ParcelableMap[] newArray(int size) {
+                    return new ParcelableMap[size];
+                }
+            };
+}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index ab4cce4..2e8faee 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -76,6 +76,7 @@
     private boolean mDropDownVerticalOffsetSet;
     private boolean mIsAnimatedFromAnchor = true;
     private boolean mOverlapAnchor;
+    private boolean mOverlapAnchorSet;
 
     private int mDropDownGravity = Gravity.NO_GRAVITY;
 
@@ -681,7 +682,9 @@
             mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
             mPopup.setTouchInterceptor(mTouchInterceptor);
             mPopup.setEpicenterBounds(mEpicenterBounds);
-            mPopup.setOverlapAnchor(mOverlapAnchor);
+            if (mOverlapAnchorSet) {
+                mPopup.setOverlapAnchor(mOverlapAnchor);
+            }
             mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset,
                     mDropDownVerticalOffset, mDropDownGravity);
             mDropDownList.setSelection(ListView.INVALID_POSITION);
@@ -1259,6 +1262,7 @@
      * @hide
      */
     public void setOverlapAnchor(boolean overlap) {
+        mOverlapAnchorSet = true;
         mOverlapAnchor = overlap;
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1b60ebc..7d9253b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -598,6 +598,11 @@
     private Layout mLayout;
     private boolean mLocalesChanged = false;
 
+    // True if setKeyListener() has been explicitly called
+    private boolean mListenerChanged = false;
+    // True if internationalized input should be used for numbers and date and time.
+    private final boolean mUseInternationalizedInput;
+
     @ViewDebug.ExportedProperty(category = "text")
     private int mGravity = Gravity.TOP | Gravity.START;
     private boolean mHorizontallyScrolling;
@@ -1356,6 +1361,9 @@
         final boolean numberPasswordInputType = variation
                 == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
 
+        mUseInternationalizedInput =
+                context.getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O;
+
         if (inputMethod != null) {
             Class<?> c;
 
@@ -1398,15 +1406,11 @@
             mEditor.mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE;
         } else if (numeric != 0) {
             createEditorIfNeeded();
-            mEditor.mKeyListener = DigitsKeyListener.getInstance((numeric & SIGNED) != 0,
-                                                   (numeric & DECIMAL) != 0);
-            inputType = EditorInfo.TYPE_CLASS_NUMBER;
-            if ((numeric & SIGNED) != 0) {
-                inputType |= EditorInfo.TYPE_NUMBER_FLAG_SIGNED;
-            }
-            if ((numeric & DECIMAL) != 0) {
-                inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL;
-            }
+            mEditor.mKeyListener = DigitsKeyListener.getInstance(
+                    mUseInternationalizedInput ? getTextLocale() : null,
+                    (numeric & SIGNED) != 0,
+                    (numeric & DECIMAL) != 0);
+            inputType = mEditor.mKeyListener.getInputType();
             mEditor.mInputType = inputType;
         } else if (autotext || autocap != -1) {
             TextKeyListener.Capitalize cap;
@@ -2308,19 +2312,13 @@
      * @attr ref android.R.styleable#TextView_autoText
      */
     public void setKeyListener(KeyListener input) {
+        mListenerChanged = true;
         setKeyListenerOnly(input);
         fixFocusableAndClickableSettings();
 
         if (input != null) {
             createEditorIfNeeded();
-            try {
-                mEditor.mInputType = mEditor.mKeyListener.getInputType();
-            } catch (IncompatibleClassChangeError e) {
-                mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
-            }
-            // Change inputType, without affecting transformation.
-            // No need to applySingleLine since mSingleLine is unchanged.
-            setInputTypeSingleLine(mSingleLine);
+            setInputTypeFromEditor();
         } else {
             if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL;
         }
@@ -2329,6 +2327,17 @@
         if (imm != null) imm.restartInput(this);
     }
 
+    private void setInputTypeFromEditor() {
+        try {
+            mEditor.mInputType = mEditor.mKeyListener.getInputType();
+        } catch (IncompatibleClassChangeError e) {
+            mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
+        }
+        // Change inputType, without affecting transformation.
+        // No need to applySingleLine since mSingleLine is unchanged.
+        setInputTypeSingleLine(mSingleLine);
+    }
+
     private void setKeyListenerOnly(KeyListener input) {
         if (mEditor == null && input == null) return; // null is the default value
 
@@ -3390,6 +3399,29 @@
         return mTextPaint.getTextLocales();
     }
 
+    private void changeListenerLocaleTo(@NonNull Locale locale) {
+        if (mListenerChanged) {
+            // If a listener has been explicitly set, don't change it. We may break something.
+            return;
+        }
+        if (mEditor != null) {
+            KeyListener listener = mEditor.mKeyListener;
+            if (listener instanceof DigitsKeyListener) {
+                listener = DigitsKeyListener.getInstance(locale, (DigitsKeyListener) listener);
+            } else if (listener instanceof DateKeyListener) {
+                listener = DateKeyListener.getInstance(locale);
+            } else if (listener instanceof TimeKeyListener) {
+                listener = TimeKeyListener.getInstance(locale);
+            } else if (listener instanceof DateTimeKeyListener) {
+                listener = DateTimeKeyListener.getInstance(locale);
+            } else {
+                return;
+            }
+            setKeyListenerOnly(listener);
+            setInputTypeFromEditor();
+        }
+    }
+
     /**
      * Set the default {@link LocaleList} of the text in this TextView to a one-member list
      * containing just the given value.
@@ -3401,6 +3433,7 @@
     public void setTextLocale(@NonNull Locale locale) {
         mLocalesChanged = true;
         mTextPaint.setTextLocale(locale);
+        changeListenerLocaleTo(locale);
         if (mLayout != null) {
             nullLayouts();
             requestLayout();
@@ -3422,6 +3455,7 @@
     public void setTextLocales(@NonNull @Size(min = 1) LocaleList locales) {
         mLocalesChanged = true;
         mTextPaint.setTextLocales(locales);
+        changeListenerLocaleTo(locales.get(0));
         if (mLayout != null) {
             nullLayouts();
             requestLayout();
@@ -3433,7 +3467,9 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         if (!mLocalesChanged) {
-            mTextPaint.setTextLocales(LocaleList.getDefault());
+            final LocaleList locales = LocaleList.getDefault();
+            mTextPaint.setTextLocales(locales);
+            changeListenerLocaleTo(locales.get(0));
             if (mLayout != null) {
                 nullLayouts();
                 requestLayout();
@@ -5567,26 +5603,35 @@
             input = TextKeyListener.getInstance(autotext, cap);
         } else if (cls == EditorInfo.TYPE_CLASS_NUMBER) {
             input = DigitsKeyListener.getInstance(
+                    mUseInternationalizedInput ? getTextLocale() : null,
                     (type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0,
                     (type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0);
+            if (mUseInternationalizedInput) {
+                type = input.getInputType(); // Override type, if necessary for i18n.
+            }
         } else if (cls == EditorInfo.TYPE_CLASS_DATETIME) {
+            final Locale locale = mUseInternationalizedInput ? getTextLocale() : null;
             switch (type & EditorInfo.TYPE_MASK_VARIATION) {
                 case EditorInfo.TYPE_DATETIME_VARIATION_DATE:
-                    input = DateKeyListener.getInstance();
+                    input = DateKeyListener.getInstance(locale);
                     break;
                 case EditorInfo.TYPE_DATETIME_VARIATION_TIME:
-                    input = TimeKeyListener.getInstance();
+                    input = TimeKeyListener.getInstance(locale);
                     break;
                 default:
-                    input = DateTimeKeyListener.getInstance();
+                    input = DateTimeKeyListener.getInstance(locale);
                     break;
             }
+            if (mUseInternationalizedInput) {
+                type = input.getInputType(); // Override type, if necessary for i18n.
+            }
         } else if (cls == EditorInfo.TYPE_CLASS_PHONE) {
             input = DialerKeyListener.getInstance();
         } else {
             input = TextKeyListener.getInstance();
         }
         setRawInputType(type);
+        mListenerChanged = false;
         if (direct) {
             createEditorIfNeeded();
             mEditor.mKeyListener = input;
diff --git a/core/java/com/android/internal/app/LRResolverRankerService.java b/core/java/com/android/internal/app/LRResolverRankerService.java
deleted file mode 100644
index 1cad7c7..0000000
--- a/core/java/com/android/internal/app/LRResolverRankerService.java
+++ /dev/null
@@ -1,199 +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 com.android.internal.app;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.storage.StorageManager;
-import android.service.resolver.ResolverRankerService;
-import android.service.resolver.ResolverTarget;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used
- * in {@link ResolverComparator}.
- */
-public final class LRResolverRankerService extends ResolverRankerService {
-    private static final String TAG = "LRResolverRankerService";
-
-    private static final boolean DEBUG = false;
-
-    private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
-    private static final String BIAS_PREF_KEY = "bias";
-    private static final String VERSION_PREF_KEY = "version";
-
-    private static final String LAUNCH_SCORE = "launch";
-    private static final String TIME_SPENT_SCORE = "timeSpent";
-    private static final String RECENCY_SCORE = "recency";
-    private static final String CHOOSER_SCORE = "chooser";
-
-    // parameters for a pre-trained model, to initialize the app ranker. When updating the
-    // pre-trained model, please update these params, as well as initModel().
-    private static final int CURRENT_VERSION = 1;
-    private static final float LEARNING_RATE = 0.0001f;
-    private static final float REGULARIZER_PARAM = 0.0001f;
-
-    private SharedPreferences mParamSharedPref;
-    private ArrayMap<String, Float> mFeatureWeights;
-    private float mBias;
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        initModel();
-        return super.onBind(intent);
-    }
-
-    @Override
-    public void onPredictSharingProbabilities(List<ResolverTarget> targets) {
-        final int size = targets.size();
-        for (int i = 0; i < size; ++i) {
-            ResolverTarget target = targets.get(i);
-            ArrayMap<String, Float> features = getFeatures(target);
-            target.setSelectProbability(predict(features));
-        }
-    }
-
-    @Override
-    public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) {
-        final int size = targets.size();
-        if (selectedPosition < 0 || selectedPosition >= size) {
-            if (DEBUG) {
-                Log.d(TAG, "Invalid Position of Selected App " + selectedPosition);
-            }
-            return;
-        }
-        final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition));
-        final float positiveProbability = targets.get(selectedPosition).getSelectProbability();
-        final int targetSize = targets.size();
-        for (int i = 0; i < targetSize; ++i) {
-            if (i == selectedPosition) {
-                continue;
-            }
-            final ArrayMap<String, Float> negative = getFeatures(targets.get(i));
-            final float negativeProbability = targets.get(i).getSelectProbability();
-            if (negativeProbability > positiveProbability) {
-                update(negative, negativeProbability, false);
-                update(positive, positiveProbability, true);
-            }
-        }
-        commitUpdate();
-    }
-
-    private void initModel() {
-        mParamSharedPref = getParamSharedPref();
-        mFeatureWeights = new ArrayMap<>(4);
-        if (mParamSharedPref == null ||
-                mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
-            // Initializing the app ranker to a pre-trained model. When updating the pre-trained
-            // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
-            // REGULARIZER_PARAM.
-            mBias = -1.6568f;
-            mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
-            mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
-            mFeatureWeights.put(RECENCY_SCORE, 0.269f);
-            mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
-        } else {
-            mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
-            mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
-            mFeatureWeights.put(
-                    TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
-            mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
-            mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
-        }
-    }
-
-    private ArrayMap<String, Float> getFeatures(ResolverTarget target) {
-        ArrayMap<String, Float> features = new ArrayMap<>(4);
-        features.put(RECENCY_SCORE, target.getRecencyScore());
-        features.put(TIME_SPENT_SCORE, target.getTimeSpentScore());
-        features.put(LAUNCH_SCORE, target.getLaunchScore());
-        features.put(CHOOSER_SCORE, target.getChooserScore());
-        return features;
-    }
-
-    private float predict(ArrayMap<String, Float> target) {
-        if (target == null) {
-            return 0.0f;
-        }
-        final int featureSize = target.size();
-        float sum = 0.0f;
-        for (int i = 0; i < featureSize; i++) {
-            String featureName = target.keyAt(i);
-            float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
-            sum += weight * target.valueAt(i);
-        }
-        return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
-    }
-
-    private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
-        if (target == null) {
-            return;
-        }
-        final int featureSize = target.size();
-        float error = isSelected ? 1.0f - predict : -predict;
-        for (int i = 0; i < featureSize; i++) {
-            String featureName = target.keyAt(i);
-            float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
-            mBias += LEARNING_RATE * error;
-            currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
-                    LEARNING_RATE * error * target.valueAt(i);
-            mFeatureWeights.put(featureName, currentWeight);
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
-        }
-    }
-
-    private void commitUpdate() {
-        try {
-            SharedPreferences.Editor editor = mParamSharedPref.edit();
-            editor.putFloat(BIAS_PREF_KEY, mBias);
-            final int size = mFeatureWeights.size();
-            for (int i = 0; i < size; i++) {
-                editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
-            }
-            editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
-            editor.apply();
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to commit update" + e);
-        }
-    }
-
-    private SharedPreferences getParamSharedPref() {
-        // The package info in the context isn't initialized in the way it is for normal apps,
-        // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
-        // build the path manually below using the same policy that appears in ContextImpl.
-        if (DEBUG) {
-            Log.d(TAG, "Context Package Name: " + getPackageName());
-        }
-        final File prefsFile = new File(new File(
-                Environment.getDataUserCePackageDirectory(
-                        StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
-                "shared_prefs"),
-                PARAM_SHARED_PREF_NAME + ".xml");
-        return getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
-    }
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 622b708..3f1c9ad 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -530,9 +530,6 @@
             getMainThreadHandler().removeCallbacks(mPostListReadyRunnable);
             mPostListReadyRunnable = null;
         }
-        if (mAdapter != null && mAdapter.mResolverListController != null) {
-            mAdapter.mResolverListController.destroy();
-        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 73b62a5..096fcb8 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -26,34 +26,20 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.SharedPreferences;
-import android.content.ServiceConnection;
 import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
 import android.os.storage.StorageManager;
 import android.os.UserHandle;
-import android.service.resolver.IResolverRankerService;
-import android.service.resolver.IResolverRankerResult;
-import android.service.resolver.ResolverRankerService;
-import android.service.resolver.ResolverTarget;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
 import java.io.File;
-import java.lang.InterruptedException;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -75,15 +61,11 @@
 
     private static final float RECENCY_MULTIPLIER = 2.f;
 
-    // message types
-    private static final int RESOLVER_RANKER_SERVICE_RESULT = 0;
-    private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1;
-
-    // timeout for establishing connections with a ResolverRankerService.
-    private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
-    // timeout for establishing connections with a ResolverRankerService, collecting features and
-    // predicting ranking scores.
-    private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
+    // feature names used in ranking.
+    private static final String LAUNCH_SCORE = "launch";
+    private static final String TIME_SPENT_SCORE = "timeSpent";
+    private static final String RECENCY_SCORE = "recency";
+    private static final String CHOOSER_SCORE = "chooser";
 
     private final Collator mCollator;
     private final boolean mHttp;
@@ -92,74 +74,18 @@
     private final Map<String, UsageStats> mStats;
     private final long mCurrentTime;
     private final long mSinceTime;
-    private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = new LinkedHashMap<>();
+    private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
     private final String mReferrerPackage;
-    private final Object mLock = new Object();
-    private ArrayList<ResolverTarget> mTargets;
     private String mContentType;
     private String[] mAnnotations;
     private String mAction;
-    private IResolverRankerService mRanker;
-    private ResolverRankerServiceConnection mConnection;
-    private AfterCompute mAfterCompute;
-    private Context mContext;
-    private CountDownLatch mConnectSignal;
+    private LogisticRegressionAppRanker mRanker;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case RESOLVER_RANKER_SERVICE_RESULT:
-                    if (DEBUG) {
-                        Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT");
-                    }
-                    if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) {
-                        if (msg.obj != null) {
-                            final List<ResolverTarget> receivedTargets =
-                                    (List<ResolverTarget>) msg.obj;
-                            if (receivedTargets != null && mTargets != null
-                                    && receivedTargets.size() == mTargets.size()) {
-                                final int size = mTargets.size();
-                                for (int i = 0; i < size; ++i) {
-                                    mTargets.get(i).setSelectProbability(
-                                            receivedTargets.get(i).getSelectProbability());
-                                }
-                            } else {
-                                Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
-                            }
-                        } else {
-                            Log.e(TAG, "Receiving null prediction results.");
-                        }
-                        mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
-                        mAfterCompute.afterCompute();
-                    }
-                    break;
-
-                case RESOLVER_RANKER_RESULT_TIMEOUT:
-                    if (DEBUG) {
-                        Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services");
-                    }
-                    mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
-                    mAfterCompute.afterCompute();
-                    break;
-
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    };
-
-    public interface AfterCompute {
-        public void afterCompute ();
-    }
-
-    public ResolverComparator(Context context, Intent intent, String referrerPackage,
-                              AfterCompute afterCompute) {
+    public ResolverComparator(Context context, Intent intent, String referrerPackage) {
         mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
         String scheme = intent.getScheme();
         mHttp = "http".equals(scheme) || "https".equals(scheme);
         mReferrerPackage = referrerPackage;
-        mAfterCompute = afterCompute;
-        mContext = context;
 
         mPm = context.getPackageManager();
         mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
@@ -170,9 +96,9 @@
         mContentType = intent.getType();
         getContentAnnotations(intent);
         mAction = intent.getAction();
+        mRanker = new LogisticRegressionAppRanker(context);
     }
 
-    // get annotations of content from intent.
     public void getContentAnnotations(Intent intent) {
         ArrayList<String> annotations = intent.getStringArrayListExtra(
                 Intent.EXTRA_CONTENT_ANNOTATIONS);
@@ -188,24 +114,20 @@
         }
     }
 
-    public void setCallBack(AfterCompute afterCompute) {
-        mAfterCompute = afterCompute;
-    }
-
-    // compute features for each target according to usage stats of targets.
     public void compute(List<ResolvedComponentInfo> targets) {
-        reset();
+        mScoredTargets.clear();
 
         final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
 
-        float mostRecencyScore = 1.0f;
-        float mostTimeSpentScore = 1.0f;
-        float mostLaunchScore = 1.0f;
-        float mostChooserScore = 1.0f;
+        long mostRecentlyUsedTime = recentSinceTime + 1;
+        long mostTimeSpent = 1;
+        int mostLaunched = 1;
+        int mostSelected = 1;
 
         for (ResolvedComponentInfo target : targets) {
-            final ResolverTarget resolverTarget = new ResolverTarget();
-            mTargetsDict.put(target.name, resolverTarget);
+            final ScoredTarget scoredTarget
+                    = new ScoredTarget(target.getResolveInfoAt(0).activityInfo);
+            mScoredTargets.put(target.name, scoredTarget);
             final UsageStats pkStats = mStats.get(target.name.getPackageName());
             if (pkStats != null) {
                 // Only count recency for apps that weren't the caller
@@ -213,33 +135,31 @@
                 // Persistent processes muck this up, so omit them too.
                 if (!target.name.getPackageName().equals(mReferrerPackage)
                         && !isPersistentProcess(target)) {
-                    final float recencyScore =
-                            (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0);
-                    resolverTarget.setRecencyScore(recencyScore);
-                    if (recencyScore > mostRecencyScore) {
-                        mostRecencyScore = recencyScore;
+                    final long lastTimeUsed = pkStats.getLastTimeUsed();
+                    scoredTarget.lastTimeUsed = lastTimeUsed;
+                    if (lastTimeUsed > mostRecentlyUsedTime) {
+                        mostRecentlyUsedTime = lastTimeUsed;
                     }
                 }
-                final float timeSpentScore = (float) pkStats.getTotalTimeInForeground();
-                resolverTarget.setTimeSpentScore(timeSpentScore);
-                if (timeSpentScore > mostTimeSpentScore) {
-                    mostTimeSpentScore = timeSpentScore;
+                final long timeSpent = pkStats.getTotalTimeInForeground();
+                scoredTarget.timeSpent = timeSpent;
+                if (timeSpent > mostTimeSpent) {
+                    mostTimeSpent = timeSpent;
                 }
-                final float launchScore = (float) pkStats.mLaunchCount;
-                resolverTarget.setLaunchScore(launchScore);
-                if (launchScore > mostLaunchScore) {
-                    mostLaunchScore = launchScore;
+                final int launched = pkStats.mLaunchCount;
+                scoredTarget.launchCount = launched;
+                if (launched > mostLaunched) {
+                    mostLaunched = launched;
                 }
 
-                float chooserScore = 0.0f;
+                int selected = 0;
                 if (pkStats.mChooserCounts != null && mAction != null
                         && pkStats.mChooserCounts.get(mAction) != null) {
-                    chooserScore = (float) pkStats.mChooserCounts.get(mAction)
-                            .getOrDefault(mContentType, 0);
+                    selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
                     if (mAnnotations != null) {
                         final int size = mAnnotations.length;
                         for (int i = 0; i < size; i++) {
-                            chooserScore += (float) pkStats.mChooserCounts.get(mAction)
+                            selected += pkStats.mChooserCounts.get(mAction)
                                     .getOrDefault(mAnnotations[i], 0);
                         }
                     }
@@ -249,37 +169,44 @@
                         Log.d(TAG, "Action type is null");
                     } else {
                         Log.d(TAG, "Chooser Count of " + mAction + ":" +
-                                target.name.getPackageName() + " is " +
-                                Float.toString(chooserScore));
+                                target.name.getPackageName() + " is " + Integer.toString(selected));
                     }
                 }
-                resolverTarget.setChooserScore(chooserScore);
-                if (chooserScore > mostChooserScore) {
-                    mostChooserScore = chooserScore;
+                scoredTarget.chooserCount = selected;
+                if (selected > mostSelected) {
+                    mostSelected = selected;
                 }
             }
         }
 
+
         if (DEBUG) {
-            Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore
-                    + " mostTimeSpentScore: " + mostTimeSpentScore
-                    + " mostLaunchScore: " + mostLaunchScore
-                    + " mostChooserScore: " + mostChooserScore);
+            Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime
+                    + " mostTimeSpent: " + mostTimeSpent
+                    + " recentSinceTime: " + recentSinceTime
+                    + " mostLaunched: " + mostLaunched);
         }
 
-        mTargets = new ArrayList<>(mTargetsDict.values());
-        for (ResolverTarget target : mTargets) {
-            final float recency = target.getRecencyScore() / mostRecencyScore;
-            setFeatures(target, recency * recency * RECENCY_MULTIPLIER,
-                    target.getLaunchScore() / mostLaunchScore,
-                    target.getTimeSpentScore() / mostTimeSpentScore,
-                    target.getChooserScore() / mostChooserScore);
-            addDefaultSelectProbability(target);
+        for (ScoredTarget target : mScoredTargets.values()) {
+            final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
+                    / (mostRecentlyUsedTime - recentSinceTime);
+            target.setFeatures((float) target.launchCount / mostLaunched,
+                    (float) target.timeSpent / mostTimeSpent,
+                    recency * recency * RECENCY_MULTIPLIER,
+                    (float) target.chooserCount / mostSelected);
+            target.selectProb = mRanker.predict(target.getFeatures());
             if (DEBUG) {
                 Log.d(TAG, "Scores: " + target);
             }
         }
-        predictSelectProbabilities(mTargets);
+    }
+
+    static boolean isPersistentProcess(ResolvedComponentInfo rci) {
+        if (rci != null && rci.getCount() > 0) {
+            return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
+                    ApplicationInfo.FLAG_PERSISTENT) != 0;
+        }
+        return false;
     }
 
     @Override
@@ -318,16 +245,16 @@
         // Pinned items stay stable within a normal lexical sort and ignore scoring.
         if (!lPinned && !rPinned) {
             if (mStats != null) {
-                final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
+                final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName(
                         lhs.activityInfo.packageName, lhs.activityInfo.name));
-                final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
+                final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
                         rhs.activityInfo.packageName, rhs.activityInfo.name));
 
-                final int selectProbabilityDiff = Float.compare(
-                        rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
+                final int selectProbDiff = Float.compare(
+                        rhsTarget.selectProb, lhsTarget.selectProb);
 
-                if (selectProbabilityDiff != 0) {
-                    return selectProbabilityDiff > 0 ? 1 : -1;
+                if (selectProbDiff != 0) {
+                    return selectProbDiff > 0 ? 1 : -1;
                 }
             }
         }
@@ -341,234 +268,177 @@
     }
 
     public float getScore(ComponentName name) {
-        final ResolverTarget target = mTargetsDict.get(name);
+        final ScoredTarget target = mScoredTargets.get(name);
         if (target != null) {
-            return target.getSelectProbability();
+            return target.selectProb;
         }
         return 0;
     }
 
+    static class ScoredTarget {
+        public final ComponentInfo componentInfo;
+        public long lastTimeUsed;
+        public long timeSpent;
+        public long launchCount;
+        public long chooserCount;
+        public ArrayMap<String, Float> features;
+        public float selectProb;
+
+        public ScoredTarget(ComponentInfo ci) {
+            componentInfo = ci;
+            features = new ArrayMap<>(5);
+        }
+
+        @Override
+        public String toString() {
+            return "ScoredTarget{" + componentInfo
+                    + " lastTimeUsed: " + lastTimeUsed
+                    + " timeSpent: " + timeSpent
+                    + " launchCount: " + launchCount
+                    + " chooserCount: " + chooserCount
+                    + " selectProb: " + selectProb
+                    + "}";
+        }
+
+        public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore,
+                                float chooserCountScore) {
+            features.put(LAUNCH_SCORE, launchCountScore);
+            features.put(TIME_SPENT_SCORE, usageTimeScore);
+            features.put(RECENCY_SCORE, recencyScore);
+            features.put(CHOOSER_SCORE, chooserCountScore);
+        }
+
+        public ArrayMap<String, Float> getFeatures() {
+            return features;
+        }
+    }
+
     public void updateChooserCounts(String packageName, int userId, String action) {
         if (mUsm != null) {
             mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
         }
     }
 
-    // update ranking model when the connection to it is valid.
     public void updateModel(ComponentName componentName) {
-        synchronized (mLock) {
-            if (mRanker != null) {
-                try {
-                    int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
-                            .indexOf(componentName);
-                    if (selectedPos > 0) {
-                        mRanker.train(mTargets, selectedPos);
-                    } else {
-                        if (DEBUG) {
-                            Log.d(TAG, "Selected a unknown component: " + componentName);
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error in Train: " + e);
-                }
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Ranker is null; skip updateModel.");
-                }
+        if (mScoredTargets == null || componentName == null ||
+                !mScoredTargets.containsKey(componentName)) {
+            return;
+        }
+        ScoredTarget selected = mScoredTargets.get(componentName);
+        for (ComponentName targetComponent : mScoredTargets.keySet()) {
+            if (targetComponent.equals(componentName)) {
+                continue;
+            }
+            ScoredTarget target = mScoredTargets.get(targetComponent);
+            // A potential point of optimization. Save updates or derive a closed form for the
+            // positive case, to avoid calculating them repeatedly.
+            if (target.selectProb >= selected.selectProb) {
+                mRanker.update(target.getFeatures(), target.selectProb, false);
+                mRanker.update(selected.getFeatures(), selected.selectProb, true);
             }
         }
+        mRanker.commitUpdate();
     }
 
-    // unbind the service and clear unhandled messges.
-    public void destroy() {
-        mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
-        mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
-        if (mConnection != null) {
-            mContext.unbindService(mConnection);
-            mConnection.destroy();
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Unbinded Resolver Ranker.");
-        }
-    }
+    class LogisticRegressionAppRanker {
+        private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
+        private static final String BIAS_PREF_KEY = "bias";
+        private static final String VERSION_PREF_KEY = "version";
 
-    // connect to a ranking service.
-    private void initRanker(Context context) {
-        synchronized (mLock) {
-            if (mConnection != null && mRanker != null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Ranker still exists; reusing the existing one.");
-                }
+        // parameters for a pre-trained model, to initialize the app ranker. When updating the
+        // pre-trained model, please update these params, as well as initModel().
+        private static final int CURRENT_VERSION = 1;
+        private static final float LEARNING_RATE = 0.0001f;
+        private static final float REGULARIZER_PARAM = 0.0001f;
+
+        private SharedPreferences mParamSharedPref;
+        private ArrayMap<String, Float> mFeatureWeights;
+        private float mBias;
+
+        public LogisticRegressionAppRanker(Context context) {
+            mParamSharedPref = getParamSharedPref(context);
+            initModel();
+        }
+
+        public float predict(ArrayMap<String, Float> target) {
+            if (target == null) {
+                return 0.0f;
+            }
+            final int featureSize = target.size();
+            float sum = 0.0f;
+            for (int i = 0; i < featureSize; i++) {
+                String featureName = target.keyAt(i);
+                float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+                sum += weight * target.valueAt(i);
+            }
+            return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
+        }
+
+        public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
+            if (target == null) {
                 return;
             }
-        }
-        Intent intent = resolveRankerService();
-        if (intent == null) {
-            return;
-        }
-        mConnectSignal = new CountDownLatch(1);
-        mConnection = new ResolverRankerServiceConnection(mConnectSignal);
-        context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
-    }
-
-    // resolve the service for ranking.
-    private Intent resolveRankerService() {
-        Intent intent = new Intent(ResolverRankerService.SERVICE_INTERFACE);
-        final List<ResolveInfo> resolveInfos = mPm.queryIntentServices(intent, 0);
-        for (ResolveInfo resolveInfo : resolveInfos) {
-            if (resolveInfo == null || resolveInfo.serviceInfo == null
-                    || resolveInfo.serviceInfo.applicationInfo == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Failed to retrieve a ranker: " + resolveInfo);
-                }
-                continue;
-            }
-            ComponentName componentName = new ComponentName(
-                    resolveInfo.serviceInfo.applicationInfo.packageName,
-                    resolveInfo.serviceInfo.name);
-            try {
-                final String perm = mPm.getServiceInfo(componentName, 0).permission;
-                if (!ResolverRankerService.BIND_PERMISSION.equals(perm)) {
-                    Log.w(TAG, "ResolverRankerService " + componentName + " does not require"
-                            + " permission " + ResolverRankerService.BIND_PERMISSION
-                            + " - this service will not be queried for ResolverComparator."
-                            + " add android:permission=\""
-                            + ResolverRankerService.BIND_PERMISSION + "\""
-                            + " to the <service> tag for " + componentName
-                            + " in the manifest.");
-                    continue;
-                }
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Could not look up service " + componentName
-                        + "; component name not found");
-                continue;
+            final int featureSize = target.size();
+            float error = isSelected ? 1.0f - predict : -predict;
+            for (int i = 0; i < featureSize; i++) {
+                String featureName = target.keyAt(i);
+                float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+                mBias += LEARNING_RATE * error;
+                currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
+                        LEARNING_RATE * error * target.valueAt(i);
+                mFeatureWeights.put(featureName, currentWeight);
             }
             if (DEBUG) {
-                Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName);
+                Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
             }
-            intent.setComponent(componentName);
-            return intent;
-        }
-        return null;
-    }
-
-    // set a watchdog, to avoid waiting for ranking service for too long.
-    private void startWatchDog(int timeOutLimit) {
-        if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms");
-        if (mHandler == null) {
-            Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
-        }
-        mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit);
-    }
-
-    private class ResolverRankerServiceConnection implements ServiceConnection {
-        private final CountDownLatch mConnectSignal;
-
-        public ResolverRankerServiceConnection(CountDownLatch connectSignal) {
-            mConnectSignal = connectSignal;
         }
 
-        public final IResolverRankerResult resolverRankerResult =
-                new IResolverRankerResult.Stub() {
-            @Override
-            public void sendResult(List<ResolverTarget> targets) throws RemoteException {
-                if (DEBUG) {
-                    Log.d(TAG, "Sending Result back to Resolver: " + targets);
-                }
-                synchronized (mLock) {
-                    final Message msg = Message.obtain();
-                    msg.what = RESOLVER_RANKER_SERVICE_RESULT;
-                    msg.obj = targets;
-                    mHandler.sendMessage(msg);
-                }
+        public void commitUpdate() {
+            SharedPreferences.Editor editor = mParamSharedPref.edit();
+            editor.putFloat(BIAS_PREF_KEY, mBias);
+            final int size = mFeatureWeights.size();
+            for (int i = 0; i < size; i++) {
+                editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
             }
-        };
+            editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
+            editor.apply();
+        }
 
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
+        private SharedPreferences getParamSharedPref(Context context) {
+            // The package info in the context isn't initialized in the way it is for normal apps,
+            // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+            // build the path manually below using the same policy that appears in ContextImpl.
             if (DEBUG) {
-                Log.d(TAG, "onServiceConnected: " + name);
+                Log.d(TAG, "Context Package Name: " + context.getPackageName());
             }
-            synchronized (mLock) {
-                mRanker = IResolverRankerService.Stub.asInterface(service);
-                mConnectSignal.countDown();
-            }
+            final File prefsFile = new File(new File(
+                    Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+                            context.getUserId(), context.getPackageName()),
+                    "shared_prefs"),
+                    PARAM_SHARED_PREF_NAME + ".xml");
+            return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
         }
 
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            if (DEBUG) {
-                Log.d(TAG, "onServiceDisconnected: " + name);
-            }
-            synchronized (mLock) {
-                destroy();
+        private void initModel() {
+            mFeatureWeights = new ArrayMap<>(4);
+            if (mParamSharedPref == null ||
+                    mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
+                // Initializing the app ranker to a pre-trained model. When updating the pre-trained
+                // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
+                // REGULARIZER_PARAM.
+                mBias = -1.6568f;
+                mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
+                mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
+                mFeatureWeights.put(RECENCY_SCORE, 0.269f);
+                mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
+            } else {
+                mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+                mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
+                mFeatureWeights.put(
+                        TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
+                mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
+                mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
             }
         }
-
-        public void destroy() {
-            synchronized (mLock) {
-                mRanker = null;
-            }
-        }
-    }
-
-    private void reset() {
-        mTargetsDict.clear();
-        mTargets = null;
-        startWatchDog(WATCHDOG_TIMEOUT_MILLIS);
-        initRanker(mContext);
-    }
-
-    // predict select probabilities if ranking service is valid.
-    private void predictSelectProbabilities(List<ResolverTarget> targets) {
-        if (mConnection == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction");
-            }
-            return;
-        } else {
-            try {
-                mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
-                synchronized (mLock) {
-                    if (mRanker != null) {
-                        mRanker.predict(targets, mConnection.resolverRankerResult);
-                        return;
-                    } else {
-                        if (DEBUG) {
-                            Log.d(TAG, "Ranker has not been initialized; skip predict.");
-                        }
-                    }
-                }
-            } catch (InterruptedException e) {
-                Log.e(TAG, "Error in Wait for Service Connection.");
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error in Predict: " + e);
-            }
-        }
-        mAfterCompute.afterCompute();
-    }
-
-    // adds select prob as the default values, according to a pre-trained Logistic Regression model.
-    private void addDefaultSelectProbability(ResolverTarget target) {
-        float sum = 2.5543f * target.getLaunchScore() + 2.8412f * target.getTimeSpentScore() +
-                0.269f * target.getRecencyScore() + 4.2222f * target.getChooserScore();
-        target.setSelectProbability((float) (1.0 / (1.0 + Math.exp(1.6568f - sum))));
-    }
-
-    // sets features for each target
-    private void setFeatures(ResolverTarget target, float recencyScore, float launchScore,
-                             float timeSpentScore, float chooserScore) {
-        target.setRecencyScore(recencyScore);
-        target.setLaunchScore(launchScore);
-        target.setTimeSpentScore(timeSpentScore);
-        target.setChooserScore(chooserScore);
-    }
-
-    static boolean isPersistentProcess(ResolvedComponentInfo rci) {
-        if (rci != null && rci.getCount() > 0) {
-            return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
-                    ApplicationInfo.FLAG_PERSISTENT) != 0;
-        }
-        return false;
     }
 }
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index e8bebb7..4071ff4 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -32,10 +32,8 @@
 import android.util.Log;
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.lang.InterruptedException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.concurrent.CountDownLatch;
 import java.util.List;
 
 /**
@@ -207,42 +205,14 @@
         return listToReturn;
     }
 
-    private class ComputeCallback implements ResolverComparator.AfterCompute {
-
-        private CountDownLatch mFinishComputeSignal;
-
-        public ComputeCallback(CountDownLatch finishComputeSignal) {
-            mFinishComputeSignal = finishComputeSignal;
-        }
-
-        public void afterCompute () {
-            mFinishComputeSignal.countDown();
-        }
-    }
-
     @VisibleForTesting
     @WorkerThread
     public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
-        final CountDownLatch finishComputeSignal = new CountDownLatch(1);
-        ComputeCallback callback = new ComputeCallback(finishComputeSignal);
         if (mResolverComparator == null) {
-            mResolverComparator =
-                    new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, callback);
-        } else {
-            mResolverComparator.setCallBack(callback);
+            mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
         }
-        try {
-            long beforeRank = System.currentTimeMillis();
-            mResolverComparator.compute(inputList);
-            finishComputeSignal.await();
-            Collections.sort(inputList, mResolverComparator);
-            long afterRank = System.currentTimeMillis();
-            if (DEBUG) {
-                Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank));
-            }
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Compute & Sort was interrupted: " + e);
-        }
+        mResolverComparator.compute(inputList);
+        Collections.sort(inputList, mResolverComparator);
     }
 
     private static boolean isSameResolvedComponent(ResolveInfo a,
@@ -263,7 +233,7 @@
     @VisibleForTesting
     public float getScore(ResolverActivity.DisplayResolveInfo target) {
         if (mResolverComparator == null) {
-            return 0.0f;
+            mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
         }
         return mResolverComparator.getScore(target.getResolvedComponentName());
     }
@@ -279,10 +249,4 @@
             mResolverComparator.updateChooserCounts(packageName, userId, action);
         }
     }
-
-    public void destroy() {
-        if (mResolverComparator != null) {
-            mResolverComparator.destroy();
-        }
-    }
 }
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index f987a9f..caf35b3 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -55,8 +55,8 @@
             in RemoteViews views);
     void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
     void notifyAppWidgetViewDataChanged(String packageName, in int[] appWidgetIds, int viewId);
-    ParceledListSlice getInstalledProvidersForProfile(int categoryFilter,
-            int profileId);
+    ParceledListSlice getInstalledProvidersForProfile(int categoryFilter, int profileId,
+            String packageName);
     AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId);
     boolean hasBindAppWidgetPermission(in String packageName, int userId);
     void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b263657..d19ffad 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -114,7 +114,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 152 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 153 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -2006,107 +2006,92 @@
      * State for keeping track of two DurationTimers with different TimeBases, presumably where one
      * TimeBase is effectively a subset of the other.
      */
-    public static class DualTimer {
-        // mMainTimer typically tracks the total time. May be pooled (but since it's a durationTimer,
-        // it also has the unpooled getTotalDurationMsLocked() for STATS_SINCE_CHARGED).
-        private final DurationTimer mMainTimer;
+    public static class DualTimer extends DurationTimer {
+        // This class both is a DurationTimer and also holds a second DurationTimer.
+        // The main timer (this) typically tracks the total time. It may be pooled (but since it's a
+        // durationTimer, it also has the unpooled getTotalDurationMsLocked() for
+        // STATS_SINCE_CHARGED).
         // mSubTimer typically tracks only part of the total time, such as background time, as
         // determined by a subTimeBase. It is NOT pooled.
         private final DurationTimer mSubTimer;
 
         /**
-         * Creates a DualTimer to hold a mMainTimer and a mSubTimer.
-         * The mMainTimer is based on the given timeBase and timerPool.
+         * Creates a DualTimer to hold a main timer (this) and a mSubTimer.
+         * The main timer (this) is based on the given timeBase and timerPool.
          * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if
-         * the mMainTimer is.
+         * the main timer is.
          */
         public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 TimeBase timeBase, TimeBase subTimeBase, Parcel in) {
-            mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase, in);
+            super(clocks, uid, type, timerPool, timeBase, in);
             mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase, in);
         }
 
         /**
-         * Creates a DualTimer to hold a mMainTimer and a mSubTimer.
-         * The mMainTimer is based on the given timeBase and timerPool.
+         * Creates a DualTimer to hold a main timer (this) and a mSubTimer.
+         * The main timer (this) is based on the given timeBase and timerPool.
          * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if
-         * the mMainTimer is.
+         * the main timer is.
          */
         public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 TimeBase timeBase, TimeBase subTimeBase) {
-            mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase);
+            super(clocks, uid, type, timerPool, timeBase);
             mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase);
         }
 
-        /** Get the main timer. */
-        public DurationTimer getMainTimer() {
-            return mMainTimer;
-        }
-
         /** Get the secondary timer. */
+        @Override
         public DurationTimer getSubTimer() {
             return mSubTimer;
         }
 
+        @Override
         public void startRunningLocked(long elapsedRealtimeMs) {
-            mMainTimer.startRunningLocked(elapsedRealtimeMs);
+            super.startRunningLocked(elapsedRealtimeMs);
             mSubTimer.startRunningLocked(elapsedRealtimeMs);
         }
 
+        @Override
         public void stopRunningLocked(long elapsedRealtimeMs) {
-            mMainTimer.stopRunningLocked(elapsedRealtimeMs);
+            super.stopRunningLocked(elapsedRealtimeMs);
             mSubTimer.stopRunningLocked(elapsedRealtimeMs);
         }
 
+        @Override
         public void stopAllRunningLocked(long elapsedRealtimeMs) {
-            mMainTimer.stopAllRunningLocked(elapsedRealtimeMs);
+            super.stopAllRunningLocked(elapsedRealtimeMs);
             mSubTimer.stopAllRunningLocked(elapsedRealtimeMs);
         }
 
-        public void setMark(long elapsedRealtimeMs) {
-            mMainTimer.setMark(elapsedRealtimeMs);
-            mSubTimer.setMark(elapsedRealtimeMs);
-        }
-
+        @Override
         public boolean reset(boolean detachIfReset) {
             boolean active = false;
-            active |= !mMainTimer.reset(detachIfReset);
+            active |= !super.reset(detachIfReset);
             active |= !mSubTimer.reset(detachIfReset);
             return !active;
         }
 
+        @Override
         public void detach() {
-            mMainTimer.detach();
+            super.detach();
             mSubTimer.detach();
         }
 
-        /**
-         * Writes a possibly null DualTimer to a Parcel.
-         *
-         * @param out the Parcel to which to write.
-         * @param t a DualTimer, or null.
-         */
-        public static void writeDualTimerToParcel(Parcel out, DualTimer t, long elapsedRealtimeUs) {
-            if (t != null) {
-                out.writeInt(1);
-                t.writeToParcel(out, elapsedRealtimeUs);
-            } else {
-                out.writeInt(0);
-            }
-        }
-
+        @Override
         public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
-            mMainTimer.writeToParcel(out, elapsedRealtimeUs);
+            super.writeToParcel(out, elapsedRealtimeUs);
             mSubTimer.writeToParcel(out, elapsedRealtimeUs);
         }
 
+        @Override
         public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
-            mMainTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
+            super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
             mSubTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
         }
 
+        @Override
         public void readSummaryFromParcelLocked(Parcel in) {
-            mMainTimer.readSummaryFromParcelLocked(in);
+            super.readSummaryFromParcelLocked(in);
             mSubTimer.readSummaryFromParcelLocked(in);
         }
     }
@@ -5488,7 +5473,7 @@
         /**
          * The statistics we have collected for this uid's jobs.
          */
-        final OverflowArrayMap<StopwatchTimer> mJobStats;
+        final OverflowArrayMap<DualTimer> mJobStats;
 
         /**
          * The statistics we have collected for this uid's sensor activations.
@@ -5533,10 +5518,10 @@
                             mBsi.mOnBatteryTimeBase);
                 }
             };
-            mJobStats = mBsi.new OverflowArrayMap<StopwatchTimer>(uid) {
-                @Override public StopwatchTimer instantiateObject() {
-                    return new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null,
-                            mBsi.mOnBatteryTimeBase);
+            mJobStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
+                @Override public DualTimer instantiateObject() {
+                    return new DualTimer(mBsi.mClocks, Uid.this, JOB, null,
+                            mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
                 }
             };
 
@@ -5918,7 +5903,7 @@
             if (mWifiScanTimer == null) {
                 return 0;
             }
-            return mWifiScanTimer.getMainTimer().getTotalTimeLocked(elapsedRealtimeUs, which);
+            return mWifiScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
         }
 
         @Override
@@ -5926,12 +5911,12 @@
             if (mWifiScanTimer == null) {
                 return 0;
             }
-            return mWifiScanTimer.getMainTimer().getCountLocked(which);
+            return mWifiScanTimer.getCountLocked(which);
         }
 
         @Override
         public int getWifiScanBackgroundCount(int which) {
-            if (mWifiScanTimer == null) {
+            if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
                 return 0;
             }
             return mWifiScanTimer.getSubTimer().getCountLocked(which);
@@ -5943,12 +5928,12 @@
                 return 0;
             }
             final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000;
-            return mWifiScanTimer.getMainTimer().getTotalDurationMsLocked(elapsedRealtimeMs) * 1000;
+            return mWifiScanTimer.getTotalDurationMsLocked(elapsedRealtimeMs) * 1000;
         }
 
         @Override
         public long getWifiScanBackgroundTime(final long elapsedRealtimeUs) {
-            if (mWifiScanTimer == null) {
+            if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) {
                 return 0;
             }
             final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000;
@@ -6008,10 +5993,7 @@
 
         @Override
         public Timer getBluetoothScanTimer() {
-            if (mBluetoothScanTimer == null) {
-                return null;
-            }
-            return mBluetoothScanTimer.getMainTimer();
+            return mBluetoothScanTimer;
         }
 
         @Override
@@ -6361,9 +6343,9 @@
                 }
             }
             mSyncStats.cleanup();
-            final ArrayMap<String, StopwatchTimer> jobStats = mJobStats.getMap();
+            final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
             for (int ij=jobStats.size()-1; ij>=0; ij--) {
-                StopwatchTimer timer = jobStats.valueAt(ij);
+                DualTimer timer = jobStats.valueAt(ij);
                 if (timer.reset(false)) {
                     jobStats.removeAt(ij);
                     timer.detach();
@@ -6531,12 +6513,12 @@
                 Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
             }
 
-            final ArrayMap<String, StopwatchTimer> jobStats = mJobStats.getMap();
+            final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
             int NJ = jobStats.size();
             out.writeInt(NJ);
             for (int ij=0; ij<NJ; ij++) {
                 out.writeString(jobStats.keyAt(ij));
-                StopwatchTimer timer = jobStats.valueAt(ij);
+                DualTimer timer = jobStats.valueAt(ij);
                 Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs);
             }
 
@@ -6756,8 +6738,8 @@
             for (int j = 0; j < numJobs; j++) {
                 String jobName = in.readString();
                 if (in.readInt() != 0) {
-                    mJobStats.add(jobName, new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null,
-                                timeBase, in));
+                    mJobStats.add(jobName, new DualTimer(mBsi.mClocks, Uid.this, JOB, null,
+                            mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in));
                 }
             }
 
@@ -7196,15 +7178,12 @@
             }
 
             void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
-                DualTimer.writeDualTimerToParcel(out, mTimer, elapsedRealtimeUs);
+                Timer.writeTimerToParcel(out, mTimer, elapsedRealtimeUs);
             }
 
             @Override
             public Timer getSensorTime() {
-                if (mTimer == null) {
-                    return null;
-                }
-                return mTimer.getMainTimer();
+                return mTimer;
             }
 
             @Override
@@ -8023,7 +8002,7 @@
         }
 
         public void readJobSummaryFromParcelLocked(String name, Parcel in) {
-            StopwatchTimer timer = mJobStats.instantiateObject();
+            DualTimer timer = mJobStats.instantiateObject();
             timer.readSummaryFromParcelLocked(in);
             mJobStats.add(name, timer);
         }
@@ -8084,14 +8063,14 @@
         }
 
         public void noteStartJobLocked(String name, long elapsedRealtimeMs) {
-            StopwatchTimer t = mJobStats.startObject(name);
+            DualTimer t = mJobStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
             }
         }
 
         public void noteStopJobLocked(String name, long elapsedRealtimeMs) {
-            StopwatchTimer t = mJobStats.stopObject(name);
+            DualTimer t = mJobStats.stopObject(name);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
             }
@@ -9149,7 +9128,7 @@
                 final Uid uid = mUidStats.valueAt(i);
 
                 // Sum the total scan power for all apps.
-                totalScanTimeMs += uid.mWifiScanTimer.getMainTimer().getTimeSinceMarkLocked(
+                totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked(
                         elapsedRealtimeMs * 1000) / 1000;
 
                 // Sum the total time holding wifi lock for all apps.
@@ -9170,7 +9149,7 @@
             for (int i = 0; i < uidStatsSize; i++) {
                 final Uid uid = mUidStats.valueAt(i);
 
-                long scanTimeSinceMarkMs = uid.mWifiScanTimer.getMainTimer().getTimeSinceMarkLocked(
+                long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
                         elapsedRealtimeMs * 1000) / 1000;
                 if (scanTimeSinceMarkMs > 0) {
                     // Set the new mark so that next time we get new data since this point.
@@ -9444,7 +9423,7 @@
                 continue;
             }
 
-            totalScanTimeMs += u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked(
+            totalScanTimeMs += u.mBluetoothScanTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000) / 1000;
         }
 
@@ -9465,7 +9444,7 @@
                 continue;
             }
 
-            long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked(
+            long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000) / 1000;
             if (scanTimeSinceMarkMs > 0) {
                 // Set the new mark so that next time we get new data since this point.
@@ -11526,7 +11505,7 @@
                 syncStats.valueAt(is).writeSummaryFromParcelLocked(out, NOWREAL_SYS);
             }
 
-            final ArrayMap<String, StopwatchTimer> jobStats = u.mJobStats.getMap();
+            final ArrayMap<String, DualTimer> jobStats = u.mJobStats.getMap();
             int NJ = jobStats.size();
             out.writeInt(NJ);
             for (int ij=0; ij<NJ; ij++) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f4dd5a6..2c8e4e0 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -236,6 +236,29 @@
         return false;
     }
 
+    public static boolean contains(@Nullable char[] array, char value) {
+        if (array == null) return false;
+        for (char element : array) {
+            if (element == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test if all {@code check} items are contained in {@code array}.
+     */
+    public static <T> boolean containsAll(@Nullable char[] array, char[] check) {
+        if (check == null) return true;
+        for (char checkItem : check) {
+            if (!contains(array, checkItem)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     public static long total(@Nullable long[] array) {
         long total = 0;
         if (array != null) {
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 1a35330..214d97c 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -68,11 +68,18 @@
     }
 
     sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
-    sk_sp<SkShader> shader = image->makeShader(
-            (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY, matrix);
+    sk_sp<SkShader> baseShader = image->makeShader(
+            (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY);
 
-    ThrowIAE_IfNull(env, shader.get());
-    return reinterpret_cast<jlong>(shader.release());
+    SkShader* shader;
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
+
+    ThrowIAE_IfNull(env, shader);
+    return reinterpret_cast<jlong>(shader);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -95,9 +102,16 @@
     #error Need to convert float array to SkScalar array before calling the following function.
 #endif
 
-    SkShader* shader = SkGradientShader::MakeLinear(pts,
+    sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts,
             reinterpret_cast<const SkColor*>(colorValues), pos, count,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
+            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
+
+    SkShader* shader;
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
 
     env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
     ThrowIAE_IfNull(env, shader);
@@ -116,8 +130,15 @@
     colors[0] = color0;
     colors[1] = color1;
 
-    SkShader* s = SkGradientShader::MakeLinear(pts, colors, NULL, 2,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
+    sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts, colors, NULL, 2,
+            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
+
+    SkShader* s;
+    if (matrix) {
+        s = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        s = baseShader.release();
+    }
 
     ThrowIAE_IfNull(env, s);
     return reinterpret_cast<jlong>(s);
@@ -141,9 +162,17 @@
     #error Need to convert float array to SkScalar array before calling the following function.
 #endif
 
-    SkShader* shader = SkGradientShader::MakeRadial(center, radius,
+    sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius,
             reinterpret_cast<const SkColor*>(colorValues), pos, count,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
+            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
+
+    SkShader* shader;
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
+
     env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
                                  JNI_ABORT);
 
@@ -161,10 +190,17 @@
     colors[0] = color0;
     colors[1] = color1;
 
-    SkShader* s = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
-            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, matrix).release();
-    ThrowIAE_IfNull(env, s);
-    return reinterpret_cast<jlong>(s);
+    sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
+            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
+
+    SkShader* shader;
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
+    ThrowIAE_IfNull(env, shader);
+    return reinterpret_cast<jlong>(shader);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -182,8 +218,17 @@
     #error Need to convert float array to SkScalar array before calling the following function.
 #endif
 
-    SkShader* shader = SkGradientShader::MakeSweep(x, y, reinterpret_cast<const SkColor*>(colors),
-            pos, count, sGradientShaderFlags, matrix).release();
+    sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y,
+            reinterpret_cast<const SkColor*>(colors), pos, count,
+            sGradientShaderFlags, NULL);
+
+    SkShader* shader;
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
+
     env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
                                  JNI_ABORT);
     ThrowIAE_IfNull(env, shader);
@@ -196,10 +241,18 @@
     SkColor colors[2];
     colors[0] = color0;
     colors[1] = color1;
-    SkShader* s = SkGradientShader::MakeSweep(x, y, colors, NULL, 2,
-            sGradientShaderFlags, matrix).release();
-    ThrowIAE_IfNull(env, s);
-    return reinterpret_cast<jlong>(s);
+
+    sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y, colors,
+            NULL, 2, sGradientShaderFlags, NULL);
+
+    SkShader* shader;
+    if (matrix) {
+        shader = baseShader->makeWithLocalMatrix(*matrix).release();
+    } else {
+        shader = baseShader.release();
+    }
+    ThrowIAE_IfNull(env, shader);
+    return reinterpret_cast<jlong>(shader);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index dae4310..520302e 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -282,6 +282,25 @@
     return mgr->configureDirectChannel(channelHandle, sensorHandle, rate);
 }
 
+static jint nativeSetOperationParameter(JNIEnv *_env, jclass _this, jlong sensorManager,
+        jint type, jfloatArray floats, jintArray ints) {
+    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+    Vector<float> floatVector;
+    Vector<int32_t> int32Vector;
+
+    if (floats != nullptr) {
+        floatVector.resize(_env->GetArrayLength(floats));
+        _env->GetFloatArrayRegion(floats, 0, _env->GetArrayLength(floats), floatVector.editArray());
+    }
+
+    if (ints != nullptr) {
+        int32Vector.resize(_env->GetArrayLength(ints));
+        _env->GetIntArrayRegion(ints, 0, _env->GetArrayLength(ints), int32Vector.editArray());
+    }
+
+    return mgr->setOperationParameter(type, floatVector, int32Vector);
+}
+
 //----------------------------------------------------------------------------
 
 class Receiver : public LooperCallback {
@@ -499,6 +518,10 @@
     {"nativeConfigDirectChannel",
             "(JIII)I",
             (void*)nativeConfigDirectChannel },
+
+    {"nativeSetOperationParameter",
+            "(JI[F[I)I",
+            (void*)nativeSetOperationParameter },
 };
 
 static const JNINativeMethod gBaseEventQueueMethods[] = {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a73f543..7922250 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3130,15 +3130,6 @@
     <permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Must be required by services that extend
-         {@link android.service.resolver.ResolverRankerService}, to ensure that only the system can
-         bind to them.
-         <p>Protection level: signature
-         @hide
-    -->
-    <permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE"
-        android:protectionLevel="signature" />
-
     <!-- Must be required by a {@link
          android.service.notification.ConditionProviderService},
          to ensure that only the system can bind to it.
@@ -3650,14 +3641,6 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
-        <service android:name="com.android.internal.app.LRResolverRankerService"
-            android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE"
-            android:exported="false"
-            android:priority="-1" >
-            <intent-filter>
-                <action android:name="android.service.resolver.ResolverRankerService" />
-            </intent-filter>
-        </service>
     </application>
 
 </manifest>
diff --git a/core/res/res/drawable/autofilled_highlight.xml b/core/res/res/drawable/autofilled_highlight.xml
new file mode 100644
index 0000000..c7aacb9
--- /dev/null
+++ b/core/res/res/drawable/autofilled_highlight.xml
@@ -0,0 +1,26 @@
+<?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.
+  -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="4dp"
+    android:insetRight="4dp"
+    android:insetBottom="4dp"
+    android:insetTop="4dp">
+    <shape>
+        <solid android:color="@color/autofilled_highlight" />
+    </shape>
+</inset>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index a165621..5a2bf4e 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,27 +17,24 @@
 
 <NotificationHeaderView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/Theme.Material.Notification"
     android:id="@+id/notification_header"
     android:orientation="horizontal"
     android:layout_width="wrap_content"
     android:layout_height="@dimen/notification_header_height"
     android:clipChildren="false"
-    android:paddingTop="@dimen/notification_header_padding_top"
-    android:paddingBottom="@dimen/notification_header_padding_bottom"
-    android:layout_marginBottom="5dp"
-    android:paddingStart="@dimen/notification_content_margin_start"
-    android:paddingEnd="16dp">
+    style="?attr/notificationHeaderStyle">
     <com.android.internal.widget.CachingIconView
         android:id="@+id/icon"
-        android:layout_width="@dimen/notification_header_icon_size"
-        android:layout_height="@dimen/notification_header_icon_size"
+        android:layout_width="?attr/notificationHeaderIconSize"
+        android:layout_height="?attr/notificationHeaderIconSize"
         android:layout_marginEnd="3dp"
         />
     <TextView
         android:id="@+id/app_name_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:textAppearance="?attr/notificationHeaderTextAppearance"
         android:layout_marginStart="3dp"
         android:layout_marginEnd="2dp"
         android:singleLine="true"
@@ -46,7 +43,7 @@
         android:id="@+id/header_text_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:textAppearance="?attr/notificationHeaderTextAppearance"
         android:layout_marginStart="2dp"
         android:layout_marginEnd="2dp"
         android:text="@string/notification_header_divider_symbol"
@@ -55,7 +52,7 @@
         android:id="@+id/header_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:textAppearance="?attr/notificationHeaderTextAppearance"
         android:layout_marginStart="2dp"
         android:layout_marginEnd="2dp"
         android:visibility="gone"
@@ -64,7 +61,7 @@
         android:id="@+id/time_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:textAppearance="?attr/notificationHeaderTextAppearance"
         android:layout_marginStart="2dp"
         android:layout_marginEnd="2dp"
         android:text="@string/notification_header_divider_symbol"
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index f3aa048..026bc6e 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -23,7 +23,8 @@
     android:paddingStart="@dimen/notification_extra_margin_ambient"
     android:paddingEnd="@dimen/notification_extra_margin_ambient"
     >
-    <include layout="@layout/notification_template_header" />
+    <include layout="@layout/notification_template_header"
+        android:theme="@style/Theme.Material.Notification.Ambient" />
 
     <LinearLayout
             android:id="@+id/notification_action_list_margin_target"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ee73b69..d26d952 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7739,13 +7739,6 @@
         <attr name="settingsActivity" />
     </declare-styleable>
 
-    <!--  TODO(b/35956626): temporary until clients change to AutofillService -->
-    <declare-styleable name="AutoFillService">
-        <!-- Fully qualified class name of an activity that allows the user to modify
-             the settings for this service. -->
-        <attr name="settingsActivity" />
-    </declare-styleable>
-
     <!-- =============================== -->
     <!-- Contacts meta-data attributes -->
     <!-- =============================== -->
@@ -8635,5 +8628,12 @@
         <attr name="stackFromEnd" format="boolean" />
     </declare-styleable>
 
+    <!-- @hide -->
+    <declare-styleable name="NotificationTheme">
+        <attr name="notificationHeaderStyle" format="reference" />
+        <attr name="notificationHeaderTextAppearance" format="reference" />
+        <attr name="notificationHeaderIconSize" format="dimension" />
+    </declare-styleable>
+
     <attr name="lockPatternStyle" format="reference" />
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index f9fd57c..937fc6f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -150,6 +150,7 @@
     <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
 
     <color name="accessibility_focus_highlight">#bf39b500</color>
+    <color name="autofilled_highlight">#4dffeb3b</color>
 
     <color name="system_notification_accent_color">#ff607D8B</color>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b82542a..c5316c6 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -191,6 +191,9 @@
     <!-- size (width and height) of the icon in the notification header -->
     <dimen name="notification_header_icon_size">18dp</dimen>
 
+    <!-- size (width and height) of the icon in the notification header -->
+    <dimen name="notification_header_icon_size_ambient">20dp</dimen>
+
     <!-- Height of a small notification in the status bar -->
     <dimen name="notification_min_height">92dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 624eb59..1d1fd5e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2825,6 +2825,10 @@
         <public name="autofill" />
     </public-group>
 
+    <public-group type="drawable" first-id="0x010800b4">
+        <public name="autofilled_highlight" />
+    </public-group>
+
 
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 831cf89..459b48f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -20,8 +20,10 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Suffix added to a number to signify size in bytes. -->
     <string name="byteShort">B</string>
-    <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). -->
-    <string name="kilobyteShort">KB</string>
+    <!-- Suffix added to a number to signify size in kilobytes (1000 bytes).
+        If you retain the Latin script for the localization, please use the lowercase
+        'k', as it signifies 1000 bytes as opposed to 1024 bytes. -->
+    <string name="kilobyteShort">kB</string>
     <!-- Suffix added to a number to signify size in megabytes. -->
     <string name="megabyteShort">MB</string>
     <!-- Suffix added to a number to signify size in gigabytes. -->
@@ -3172,7 +3174,7 @@
     <string name="alert_windows_notification_channel_name"><xliff:g id="name" example="Google Maps">%s</xliff:g> displaying over other apps</string>
     <!-- Notification title when an application is displaying ui on-top of other apps
          [CHAR LIMIT=30] -->
-    <string name="alert_windows_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is displaying over other apps.</string>
+    <string name="alert_windows_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is displaying over other apps</string>
     <!-- Notification body when an application is displaying ui on-top of other apps
          [CHAR LIMIT=NONE] -->
     <string name="alert_windows_notification_message">If you don’t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string>
@@ -4600,6 +4602,10 @@
     <string name="autofill_save_type_address">address</string>
     <!-- Label for the type of data being saved for autofill when it represents a credit card [CHAR LIMIT=NONE] -->
     <string name="autofill_save_type_credit_card">credit card</string>
+    <!-- Label for the type of data being saved for autofill when it represents an username [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_type_username">username</string>
+    <!-- Label for the type of data being saved for autofill when it represents an email address [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_type_email_address">email address</string>
 
     <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for earthquake -->
     <string name="etws_primary_default_message_earthquake">Stay calm and seek shelter nearby.</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 8f061a3..ec16611 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -488,6 +488,10 @@
 
     <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" />
 
+    <style name="TextAppearance.Material.Notification.Info.Ambient">
+        <item name="textSize">@dimen/notification_text_size</item>
+    </style>
+
     <style name="TextAppearance.Material.Notification.Emphasis">
         <item name="textColor">#66000000</item>
     </style>
@@ -1283,4 +1287,12 @@
 
     <style name="DialogWindowTitle.Material.Light" />
 
+    <style name="Notification.Header" parent="">
+        <item name="paddingTop">@dimen/notification_header_padding_top</item>
+        <item name="paddingBottom">@dimen/notification_header_padding_bottom</item>
+        <item name="layout_marginBottom">5dp</item>
+        <item name="paddingStart">@dimen/notification_content_margin_start</item>
+        <item name="paddingEnd">16dp</item>
+    </style>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c12116a..428db87 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1333,6 +1333,7 @@
   <java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" />
 
   <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
+  <java-symbol type="drawable" name="autofilled_highlight"/>
 
   <java-symbol type="drawable" name="ic_account_circle" />
   <java-symbol type="color" name="user_icon_1" />
@@ -2883,6 +2884,8 @@
   <java-symbol type="string" name="autofill_save_type_password" />
   <java-symbol type="string" name="autofill_save_type_address" />
   <java-symbol type="string" name="autofill_save_type_credit_card" />
+  <java-symbol type="string" name="autofill_save_type_username" />
+  <java-symbol type="string" name="autofill_save_type_email_address" />
 
   <!-- Accessibility fingerprint gestures -->
   <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 008c817..9dafa7a 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1321,6 +1321,19 @@
         <item name="windowNoTitle">true</item>
     </style>
 
+    <!-- Theme for inflating notifications -->
+    <style name="Theme.Material.Notification" parent="">
+        <item name="notificationHeaderStyle">@style/Notification.Header</item>
+        <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info</item>
+        <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item>
+    </style>
+
+    <!-- Theme for inflating ambient notification -->
+    <style name="Theme.Material.Notification.Ambient">
+        <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item>
+        <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item>
+    </style>
+
     <!-- Default theme for Settings and activities launched from Settings. -->
     <style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
         <item name="colorPrimary">@color/primary_material_settings_light</item>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index 6b52b98..8283335 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -21,6 +21,7 @@
 import android.os.BatteryStats;
 import android.os.WorkSource;
 import android.support.test.filters.SmallTest;
+import android.util.ArrayMap;
 
 import junit.framework.TestCase;
 
@@ -187,4 +188,65 @@
         assertEquals((305 - 202) * 1000, actualTime);
         assertEquals((305 - 254) * 1000, bgTime);
     }
+
+    @SmallTest
+    public void testJob() throws Exception {
+        final MockClocks clocks = new MockClocks();
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+        final String jobName = "job_name";
+        long curr = 0; // realtime in us
+
+        // On battery
+        curr = 1000 * (clocks.realtime = clocks.uptime = 100);
+        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+        // App in foreground
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+
+        // Start timer
+        curr = 1000 * (clocks.realtime = clocks.uptime = 151);
+        bi.noteJobStartLocked(jobName, UID);
+
+        // Stop timer
+        curr = 1000 * (clocks.realtime = clocks.uptime = 161);
+        bi.noteJobFinishLocked(jobName, UID);
+
+        // Start timer
+        curr = 1000 * (clocks.realtime = clocks.uptime = 202);
+        bi.noteJobStartLocked(jobName, UID);
+
+        // Move to background
+        curr = 1000 * (clocks.realtime = clocks.uptime = 254);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+        // Off battery
+        curr = 1000 * (clocks.realtime = clocks.uptime = 305);
+        bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+
+        // Stop timer
+        curr = 1000 * (clocks.realtime = clocks.uptime = 409);
+        bi.noteJobFinishLocked(jobName, UID);
+
+        // Test
+        curr = 1000 * (clocks.realtime = clocks.uptime = 657);
+        final ArrayMap<String, ? extends BatteryStats.Timer> jobs =
+                bi.getUidStats().get(UID).getJobStats();
+        assertEquals(1, jobs.size());
+        BatteryStats.Timer timer = jobs.valueAt(0);
+        BatteryStats.Timer bgTimer = timer.getSubTimer();
+        long time = timer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED);
+        int count = timer.getCountLocked(STATS_SINCE_CHARGED);
+        int bgCount = bgTimer.getCountLocked(STATS_SINCE_CHARGED);
+        long bgTime = bgTimer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED);
+        assertEquals((161 - 151 + 305 - 202) * 1000, time);
+        assertEquals(2, count);
+        assertEquals(1, bgCount);
+        assertEquals((305 - 254) * 1000, bgTime);
+
+        // Test that a second job is separate.
+        curr = 1000 * (clocks.realtime = clocks.uptime = 3000);
+        final String jobName2 = "second_job";
+        bi.noteJobStartLocked(jobName2, UID);
+        assertEquals(2, bi.getUidStats().get(UID).getJobStats().size());
+        bi.noteJobFinishLocked(jobName2, UID);
+    }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2723826..97dd933 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -753,9 +753,12 @@
      * @param init container-specific data, its meaning is interpreted based on the
      * mime type provided in the mimeType parameter.  It could contain, for example,
      * the content ID, key ID or other data obtained from the content metadata that is
-     * required in generating the key request. init may be null when keyType is
-     * KEY_TYPE_RELEASE.
-     * @param mimeType identifies the mime type of the content
+     * required in generating the key request. May be null when keyType is
+     * KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the first key
+     * request for the session.
+     * @param mimeType identifies the mime type of the content. May be null if the
+     * keyType is KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the
+     * first key request for the session.
      * @param keyType specifes the type of the request. The request may be to acquire
      * keys for streaming or offline content, or to release previously acquired
      * keys, which are identified by a keySetId.
@@ -779,13 +782,17 @@
      * response is for an offline key request, a keySetId is returned that can be
      * used to later restore the keys to a new session with the method
      * {@link #restoreKeys}.
-     * When the response is for a streaming or release request, null is returned.
+     * When the response is for a streaming or release request, an empty byte array
+     * is returned.
      *
      * @param scope may be a sessionId or keySetId depending on the type of the
      * response.  Scope should be set to the sessionId when the response is for either
      * streaming or offline key requests.  Scope should be set to the keySetId when
      * the response is for a release request.
      * @param response the byte array response from the server
+     * @return If the response is for an offline request, the keySetId for the offline
+     * keys will be returned. If the response is for a streaming or release request
+     * an empty byte array will be returned.
      *
      * @throws NotProvisionedException if the response indicates that
      * reprovisioning is required
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 23bf3d6..431d5d8 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -34,6 +34,7 @@
     libutils \
     libbinder \
     libmedia \
+    libmediametrics \
     libmediadrm \
     libmidi \
     libskia \
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index f9342b7..fa5c3ff 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -6,6 +6,7 @@
     <string name="no_data_notification_id">Your mobile data has been deactivated</string>
     <string name="portal_notification_detail">Tap to visit the %s website</string>
     <string name="no_data_notification_detail">Please contact your service provider %s</string>
+    <string name="mobile_data_status_notification_channel_name">Mobile data status</string>
     <string name="action_bar_label">Sign in to mobile network</string>
     <string name="ssl_error_warning">The network you&#8217;re trying to join has security issues.</string>
     <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 73ff3a9..7fd1601 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -16,6 +16,7 @@
 package com.android.carrierdefaultapp;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -35,6 +36,7 @@
 
     private static final String PORTAL_NOTIFICATION_TAG = "CarrierDefault.Portal.Notification";
     private static final String NO_DATA_NOTIFICATION_TAG = "CarrierDefault.NoData.Notification";
+    private static final String NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS = "mobile_data_status";
     private static final int PORTAL_NOTIFICATION_ID = 0;
     private static final int NO_DATA_NOTIFICATION_ID = 1;
     private static boolean ENABLE = true;
@@ -150,9 +152,18 @@
     private static Notification getNotification(Context context, int titleId, int textId,
                                          PendingIntent pendingIntent) {
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+        final NotificationManager notificationManager = context.getSystemService(
+                NotificationManager.class);
         final Resources resources = context.getResources();
         final Bundle extras = Bundle.forPair(Notification.EXTRA_SUBSTITUTE_APP_NAME,
                 resources.getString(R.string.android_system_label));
+        /* Creates the notification channel and registers it with NotificationManager. If a channel
+         * with the same ID is already registered, NotificationManager will ignore this call.
+         */
+        notificationManager.createNotificationChannel(new NotificationChannel(
+                NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS,
+                resources.getString(R.string.mobile_data_status_notification_channel_name),
+                NotificationManager.IMPORTANCE_DEFAULT));
         Notification.Builder builder = new Notification.Builder(context)
                 .setContentTitle(resources.getString(titleId))
                 .setContentText(String.format(resources.getString(textId),
@@ -167,7 +178,8 @@
                 .setLocalOnly(true)
                 .setWhen(System.currentTimeMillis())
                 .setShowWhen(false)
-                .setExtras(extras);
+                .setExtras(extras)
+                .setChannel(NOTIFICATION_CHANNEL_ID_MOBILE_DATA_STATUS);
 
         if (pendingIntent != null) {
             builder.setContentIntent(pendingIntent);
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 2e6ed69b..63850ae 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -149,6 +149,12 @@
     <!-- Description of printer info icon. [CHAR LIMIT=50] -->
     <string name="printer_info_desc">More information about this printer</string>
 
+    <!-- Label for the notification channel that contains print jobs without problems. [CHAR LIMIT=40] -->
+    <string name="notification_channel_progress">Running print jobs</string>
+
+    <!-- Label for the notification channel that contains print jobs with problems. [CHAR LIMIT=40] -->
+    <string name="notification_channel_failure">Failed print jobs</string>
+
     <!-- Notification that we could not create a file name for the printed PDF. [CHAR LIMIT=50] -->
     <string name="could_not_create_file">Could not create file</string>
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index cd1d540..9d737e0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -20,13 +20,12 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Notification.Action;
-import android.app.Notification.InboxStyle;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -57,14 +56,14 @@
 
     public static final String LOG_TAG = "NotificationController";
 
+    private static final String NOTIFICATION_CHANNEL_PROGRESS = "PRINT_PROGRESS";
+    private static final String NOTIFICATION_CHANNEL_FAILURES = "PRINT_FAILURES";
+
     private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB";
     private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
 
     private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
 
-    private static final String PRINT_JOB_NOTIFICATION_GROUP_KEY = "PRINT_JOB_NOTIFICATIONS";
-    private static final String PRINT_JOB_NOTIFICATION_SUMMARY = "PRINT_JOB_NOTIFICATIONS_SUMMARY";
-
     private final Context mContext;
     private final NotificationManager mNotificationManager;
 
@@ -78,6 +77,15 @@
         mNotificationManager = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mNotifications = new ArraySet<>(0);
+
+        mNotificationManager.createNotificationChannel(
+                new NotificationChannel(NOTIFICATION_CHANNEL_PROGRESS,
+                        context.getString(R.string.notification_channel_progress),
+                        NotificationManager.IMPORTANCE_LOW));
+        mNotificationManager.createNotificationChannel(
+                new NotificationChannel(NOTIFICATION_CHANNEL_FAILURES,
+                        context.getString(R.string.notification_channel_failure),
+                        NotificationManager.IMPORTANCE_DEFAULT));
     }
 
     public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
@@ -104,13 +112,6 @@
 
         final int numPrintJobs = printJobs.size();
 
-        // Create summary notification
-        if (numPrintJobs > 1) {
-            createStackedNotification(printJobs);
-        } else {
-            mNotificationManager.cancel(PRINT_JOB_NOTIFICATION_SUMMARY, 0);
-        }
-
         // Create per print job notification
         for (int i = 0; i < numPrintJobs; i++) {
             PrintJobInfo printJob = printJobs.get(i);
@@ -178,16 +179,16 @@
      */
     private void createNotification(@NonNull PrintJobInfo printJob, @Nullable Action firstAction,
             @Nullable Action secondAction) {
-        Notification.Builder builder = new Notification.Builder(mContext)
+        Notification.Builder builder = new Notification.Builder(mContext, computeChannel(printJob))
                 .setContentIntent(createContentIntent(printJob.getId()))
                 .setSmallIcon(computeNotificationIcon(printJob))
                 .setContentTitle(computeNotificationTitle(printJob))
                 .setWhen(System.currentTimeMillis())
                 .setOngoing(true)
                 .setShowWhen(true)
+                .setOnlyAlertOnce(true)
                 .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY);
+                        com.android.internal.R.color.system_notification_accent_color));
 
         if (firstAction != null) {
             builder.addAction(firstAction);
@@ -238,43 +239,6 @@
         createNotification(printJob, null, null);
     }
 
-    private void createStackedNotification(List<PrintJobInfo> printJobs) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                .setContentIntent(createContentIntent(null))
-                .setWhen(System.currentTimeMillis())
-                .setOngoing(true)
-                .setShowWhen(true)
-                .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY)
-                .setGroupSummary(true);
-
-        final int printJobCount = printJobs.size();
-
-        InboxStyle inboxStyle = new InboxStyle();
-
-        int icon = com.android.internal.R.drawable.ic_print;
-        for (int i = printJobCount - 1; i>= 0; i--) {
-            PrintJobInfo printJob = printJobs.get(i);
-
-            inboxStyle.addLine(computeNotificationTitle(printJob));
-
-            // if any print job is in an error state show an error icon for the summary
-            if (printJob.getState() == PrintJobInfo.STATE_FAILED
-                    || printJob.getState() == PrintJobInfo.STATE_BLOCKED) {
-                icon = com.android.internal.R.drawable.ic_print_error;
-            }
-        }
-
-        builder.setSmallIcon(icon);
-        builder.setLargeIcon(
-                ((BitmapDrawable) mContext.getResources().getDrawable(icon, null)).getBitmap());
-        builder.setNumber(printJobCount);
-        builder.setStyle(inboxStyle);
-        builder.setColor(mContext.getColor(
-                com.android.internal.R.color.system_notification_accent_color));
-
-        mNotificationManager.notify(PRINT_JOB_NOTIFICATION_SUMMARY, 0, builder.build());
-    }
-
     private String computeNotificationTitle(PrintJobInfo printJob) {
         switch (printJob.getState()) {
             case PrintJobInfo.STATE_FAILED: {
@@ -359,6 +323,22 @@
         }
     }
 
+    private static String computeChannel(PrintJobInfo printJob) {
+        if (printJob.isCancelling()) {
+            return NOTIFICATION_CHANNEL_PROGRESS;
+        }
+
+        switch (printJob.getState()) {
+            case PrintJobInfo.STATE_FAILED:
+            case PrintJobInfo.STATE_BLOCKED: {
+                return NOTIFICATION_CHANNEL_FAILURES;
+            }
+            default: {
+                return NOTIFICATION_CHANNEL_PROGRESS;
+            }
+        }
+    }
+
     public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
         @SuppressWarnings("hiding")
         private static final String LOG_TAG = "NotificationBroadcastReceiver";
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 67ef40a..1ad4fea 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -6,6 +6,7 @@
 LOCAL_MODULE := SettingsLib
 
 LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations \
     android-support-v4 \
     android-support-v7-recyclerview \
     android-support-v7-preference \
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 4bfca9b..474de90 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -211,7 +211,16 @@
         if (preferLongName) {
             displayName = getZoneLongName(timeZoneNames, tz, now);
         } else {
-            displayName = timeZoneNames.getExemplarLocationName(tz.getID());
+            // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs
+            // that match ICUs zone IDs (which are similar but not guaranteed the same as those
+            // in timezones.xml). timezones.xml and related files uses the IANA IDs. ICU IDs are
+            // stable and IANA IDs have changed over time so they have drifted.
+            // See http://bugs.icu-project.org/trac/ticket/13070 / http://b/36469833.
+            String canonicalZoneId = android.icu.util.TimeZone.getCanonicalID(tz.getID());
+            if (canonicalZoneId == null) {
+                canonicalZoneId = tz.getID();
+            }
+            displayName = timeZoneNames.getExemplarLocationName(canonicalZoneId);
             if (displayName == null || displayName.isEmpty()) {
                 // getZoneExemplarLocation can return null. Fall back to the long name.
                 displayName = getZoneLongName(timeZoneNames, tz, now);
@@ -325,4 +334,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
index 40abb6c..88f133c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
@@ -41,8 +41,8 @@
         long privateTotalBytes = 0;
         for (VolumeInfo info : sm.getVolumes()) {
             if (info.getType() == VolumeInfo.TYPE_PRIVATE && info.isMountedReadable()) {
-                privateTotalBytes += stats.getTotalBytes(info.getFsUuid());
-                privateFreeBytes += stats.getFreeBytes(info.getFsUuid());
+                privateTotalBytes += sm.getTotalBytes(stats, info);
+                privateFreeBytes += sm.getFreeBytes(stats, info);
             }
         }
         return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes);
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
index 320494c..11060e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.deviceinfo;
 
+import android.app.usage.StorageStatsManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 
@@ -46,4 +47,14 @@
     public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) {
         return mStorageManager.findEmulatedForPrivate(privateVolume);
     }
+
+    @Override
+    public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) {
+        return stats.getTotalBytes(volume.getFsUuid());
+    }
+
+    @Override
+    public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) {
+        return stats.getFreeBytes(volume.getFsUuid());
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
index 646c42f..e5d85d1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.deviceinfo;
 
+import android.app.usage.StorageStatsManager;
 import android.os.storage.VolumeInfo;
 
 import java.util.List;
@@ -39,4 +40,18 @@
      * Returns the emulated volume for a given private volume.
      */
     VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume);
+
+    /**
+     * Returns the total bytes for a given storage volume.
+     *
+     * @pre The volume is a private volume and is readable.
+     */
+    long getTotalBytes(StorageStatsManager stats, VolumeInfo volume);
+
+    /**
+     * Returns the free bytes for a given storage volume.
+     *
+     * @pre The volume is a private volume and is readable.
+     */
+    long getFreeBytes(StorageStatsManager stats, VolumeInfo volume);
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 1ea4183..901848a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -149,6 +149,7 @@
 
     private int mRankingScore = Integer.MIN_VALUE;
     private int mBadge = NetworkBadging.BADGING_NONE;
+    private boolean mIsScoredNetworkMetered = false;
 
     // used to co-relate internal vs returned accesspoint.
     int mId;
@@ -248,6 +249,7 @@
         this.mScanResultCache.putAll(that.mScanResultCache);
         this.mId = that.mId;
         this.mBadge = that.mBadge;
+        this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered;
         this.mRankingScore = that.mRankingScore;
     }
 
@@ -336,16 +338,32 @@
         builder.append(",level=").append(getLevel());
         builder.append(",rankingScore=").append(mRankingScore);
         builder.append(",badge=").append(mBadge);
+        builder.append(",metered=").append(isMetered());
 
         return builder.append(')').toString();
     }
 
     /**
+     * Updates the AccessPoint rankingScore, metering, and badge, returning true if the data has
+     * changed.
+     *
+     * @param scoreCache The score cache to use to retrieve scores.
+     * @param scoringUiEnabled Whether to show scoring and badging UI.
+     */
+    boolean update(WifiNetworkScoreCache scoreCache, boolean scoringUiEnabled) {
+        boolean scoreChanged = false;
+        if (scoringUiEnabled) {
+            scoreChanged = updateScores(scoreCache);
+        }
+        return updateMetered(scoreCache) || scoreChanged;
+    }
+
+    /**
      * Updates the AccessPoint rankingScore and badge, returning true if the data has changed.
      *
      * @param scoreCache The score cache to use to retrieve scores.
      */
-    boolean updateScores(WifiNetworkScoreCache scoreCache) {
+    private boolean updateScores(WifiNetworkScoreCache scoreCache) {
         int oldBadge = mBadge;
         int oldRankingScore = mRankingScore;
         mBadge = NetworkBadging.BADGING_NONE;
@@ -366,6 +384,23 @@
         return (oldBadge != mBadge || oldRankingScore != mRankingScore);
     }
 
+    /**
+     * Updates the AccessPoint's metering based on {@link ScoredNetwork#meteredHint}, returning
+     * true if the metering changed.
+     */
+    private boolean updateMetered(WifiNetworkScoreCache scoreCache) {
+        boolean oldMetering = mIsScoredNetworkMetered;
+        mIsScoredNetworkMetered = false;
+        for (ScanResult result : mScanResultCache.values()) {
+            ScoredNetwork score = scoreCache.getScoredNetwork(result);
+            if (score == null) {
+                continue;
+            }
+            mIsScoredNetworkMetered |= score.meteredHint;
+        }
+        return oldMetering == mIsScoredNetworkMetered;
+    }
+
     private void evictOldScanResults() {
         long nowMs = SystemClock.elapsedRealtime();
         for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
@@ -474,6 +509,17 @@
         mSeen = seen;
     }
 
+    /**
+     * Returns if the network is marked metered. Metering can be marked through its config in
+     * {@link WifiConfiguration}, after connection in {@link WifiInfo}, or from a score config in
+     * {@link ScoredNetwork}.
+     */
+    public boolean isMetered() {
+        return mIsScoredNetworkMetered
+                || (mConfig != null && mConfig.meteredHint)
+                || (mInfo != null && mInfo.getMeteredHint());
+    }
+
     public NetworkInfo getNetworkInfo() {
         return mNetworkInfo;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 8f8167e..e82bf81 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -183,7 +183,7 @@
         }
         if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
             mFrictionSld.setState(STATE_SECURED);
-        } else if (mAccessPoint.getConfig() != null && mAccessPoint.getConfig().meteredHint) {
+        } else if (mAccessPoint.isMetered()) {
             mFrictionSld.setState(STATE_METERED);
         }
         Drawable drawable = mFrictionSld.getCurrent();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 50f294c..fc8c42c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -543,11 +543,9 @@
             }
         }
 
-        if (mNetworkScoringUiEnabled) {
-            requestScoresForNetworkKeys(scoresToRequest);
-            for (AccessPoint ap : accessPoints) {
-                ap.updateScores(mScoreCache);
-            }
+        requestScoresForNetworkKeys(scoresToRequest);
+        for (AccessPoint ap : accessPoints) {
+            ap.update(mScoreCache, mNetworkScoringUiEnabled);
         }
 
         // Pre-sort accessPoints to speed preference insertion
@@ -648,7 +646,7 @@
             if (ap.update(connectionConfig, mLastInfo, mLastNetworkInfo)) {
                 reorder = true;
             }
-            if (mNetworkScoringUiEnabled && ap.updateScores(mScoreCache)) {
+            if (ap.update(mScoreCache, mNetworkScoringUiEnabled)) {
                 reorder = true;
             }
         }
@@ -659,15 +657,11 @@
     }
 
     /**
-     * Update all the internal access points rankingScores and badge.
+     * Update all the internal access points rankingScores, badge and metering.
      *
      * <p>Will trigger a resort and notify listeners of changes if applicable.
      */
     private void updateNetworkScores() {
-        if (!mNetworkScoringUiEnabled) {
-            return;
-        }
-
         // Lock required to prevent accidental copying of AccessPoint states while the modification
         // is in progress. see #copyAndNotifyListeners
         long before = System.currentTimeMillis();
@@ -679,7 +673,7 @@
 
         boolean reorder = false;
         for (int i = 0; i < mInternalAccessPoints.size(); i++) {
-            if (mInternalAccessPoints.get(i).updateScores(mScoreCache)) {
+            if (mInternalAccessPoints.get(i).update(mScoreCache, mNetworkScoringUiEnabled)) {
                 reorder = true;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
index d3bdeb7..ab7c6d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.os.Looper;
+import android.support.annotation.Keep;
 
 /**
  * Factory method used to inject WifiTracker instances.
@@ -26,14 +27,7 @@
 
     private static WifiTracker sTestingWifiTracker;
 
-    public static void enableTestingMode() {
-        sTestingMode = true;
-    }
-
-    public static void disableTestingMode() {
-        sTestingMode = false;
-    }
-
+    @Keep
     public static void setTestingWifiTracker(WifiTracker tracker) {
         sTestingWifiTracker = tracker;
     }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 762d9f8..3e01b34 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -17,15 +17,19 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.NetworkKey;
+import android.net.ScoredNetwork;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.HomeSp;
@@ -36,10 +40,11 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.text.SpannableString;
 import android.text.style.TtsSpan;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -50,9 +55,11 @@
 
     private static final String TEST_SSID = "test_ssid";
     private Context mContext;
+    @Mock private WifiNetworkScoreCache mWifiNetworkScoreCache;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mContext = InstrumentationRegistry.getTargetContext();
     }
 
@@ -74,6 +81,7 @@
     @Test
     public void testCopyAccessPoint_dataShouldMatch() {
         WifiConfiguration configuration = createWifiConfiguration();
+        configuration.meteredHint = true;
 
         NetworkInfo networkInfo =
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
@@ -88,6 +96,7 @@
         assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid());
         assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig());
         assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity());
+        assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered());
         assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue();
     }
 
@@ -230,6 +239,55 @@
         assertTrue(ap.isPasspointConfig());
     }
 
+    @Test
+    public void testIsMetered_returnTrueWhenWifiConfigurationIsMetered() {
+        WifiConfiguration configuration = createWifiConfiguration();
+        configuration.meteredHint = true;
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
+        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+        wifiInfo.setBSSID(configuration.BSSID);
+        wifiInfo.setNetworkId(configuration.networkId);
+        accessPoint.update(configuration, wifiInfo, networkInfo);
+
+        assertTrue(accessPoint.isMetered());
+    };
+
+    @Test
+    public void testIsMetered_returnTrueWhenWifiInfoIsMetered() {
+        WifiConfiguration configuration = createWifiConfiguration();
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
+        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+        wifiInfo.setBSSID(configuration.BSSID);
+        wifiInfo.setNetworkId(configuration.networkId);
+        wifiInfo.setMeteredHint(true);
+        accessPoint.update(configuration, wifiInfo, networkInfo);
+
+        assertTrue(accessPoint.isMetered());
+    };
+
+    @Test
+    public void testIsMetered_returnTrueWhenScoredNetworkIsMetered() {
+        AccessPoint ap = createAccessPointWithScanResultCache();
+
+        when(mWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
+                .thenReturn(
+                        new ScoredNetwork(
+                                null /* NetworkKey */,
+                                null /* rssiCurve */,
+                                true /* metered */));
+        ap.update(mWifiNetworkScoreCache, false /* scoringUiEnabled */);
+
+        assertTrue(ap.isMetered());
+    };
+
     private AccessPoint createAccessPointWithScanResultCache() {
         Bundle bundle = new Bundle();
         ArrayList<ScanResult> scanResults = new ArrayList<>();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 02deb44..b71915f 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import static org.mockito.Mockito.any;
@@ -302,7 +303,7 @@
                 new ScoredNetwork(
                         NETWORK_KEY_2,
                         mockCurve2,
-                        false /* meteredHint */,
+                        true /* meteredHint */,
                         attr2);
 
         WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
@@ -515,6 +516,23 @@
     }
 
     @Test
+    public void scoreCacheUpdateMeteredShouldUpdateAccessPointMetering()
+            throws InterruptedException {
+        WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
+        updateScoresAndWaitForAccessPointsChangedCallback();
+
+        List<AccessPoint> aps = tracker.getAccessPoints();
+
+        for (AccessPoint ap : aps) {
+            if (ap.getSsidStr().equals(SSID_1)) {
+                assertFalse(ap.isMetered());
+            } else if (ap.getSsidStr().equals(SSID_2)) {
+                assertTrue(ap.isMetered());
+            }
+        }
+    }
+
+    @Test
     public void noBadgesShouldBeInsertedIntoAccessPointWhenScoringUiDisabled()
             throws InterruptedException {
         Settings.Global.putInt(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1f559e4..169a034 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -994,6 +994,15 @@
         return false;
     }
 
+    private PackageInfo getCallingPackageInfo(int userId) {
+        try {
+            return mPackageManager.getPackageInfo(getCallingPackage(),
+                    PackageManager.GET_SIGNATURES, userId);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Package " + getCallingPackage() + " doesn't exist");
+        }
+    }
+
     private Cursor getAllSecureSettings(int userId, String[] projection) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getAllSecureSettings(" + userId + ")");
@@ -1002,6 +1011,13 @@
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
+        // The relevant "calling package" userId will be the owning userId for some
+        // profiles, and we can't do the lookup inside our [lock held] loop, so work out
+        // up front who the effective "new SSAID" user ID for that settings name will be.
+        final int ssaidUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId,
+                Settings.Secure.ANDROID_ID);
+        final PackageInfo ssaidCallingPkg = getCallingPackageInfo(ssaidUserId);
+
         synchronized (mLock) {
             List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId);
 
@@ -1026,7 +1042,7 @@
                 // SETTINGS_FILE_SSAID, unless accessed by a system process.
                 final Setting setting;
                 if (isNewSsaidSetting(name)) {
-                    setting = getSsaidSettingLocked(owningUserId);
+                    setting = getSsaidSettingLocked(ssaidCallingPkg, owningUserId);
                 } else {
                     setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId,
                             name);
@@ -1060,14 +1076,17 @@
             return settings != null ? settings.getNullSetting() : null;
         }
 
-        // Get the value.
-        synchronized (mLock) {
-            // As of Android O, the SSAID is read from an app-specific entry in table
-            // SETTINGS_FILE_SSAID, unless accessed by a system process.
-            if (isNewSsaidSetting(name)) {
-                return getSsaidSettingLocked(owningUserId);
+        // As of Android O, the SSAID is read from an app-specific entry in table
+        // SETTINGS_FILE_SSAID, unless accessed by a system process.
+        if (isNewSsaidSetting(name)) {
+            PackageInfo callingPkg = getCallingPackageInfo(owningUserId);
+            synchronized (mLock) {
+                return getSsaidSettingLocked(callingPkg, owningUserId);
             }
+        }
 
+        // Not the SSAID; do a straight lookup
+        synchronized (mLock) {
             return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE,
                     owningUserId, name);
         }
@@ -1078,7 +1097,7 @@
                 && UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID;
     }
 
-    private Setting getSsaidSettingLocked(int owningUserId) {
+    private Setting getSsaidSettingLocked(PackageInfo callingPkg, int owningUserId) {
         // Get uid of caller (key) used to store ssaid value
         String name = Integer.toString(
                 UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid())));
@@ -1093,7 +1112,7 @@
 
         // Lazy initialize ssaid if not yet present in ssaid table.
         if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
-            return mSettingsRegistry.generateSsaidLocked(getCallingPackage(), owningUserId);
+            return mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
         }
 
         return ssaid;
@@ -2070,15 +2089,7 @@
             return ByteBuffer.allocate(4).putInt(data.length).array();
         }
 
-        public Setting generateSsaidLocked(String packageName, int userId) {
-            final PackageInfo packageInfo;
-            try {
-                packageInfo = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_SIGNATURES, userId);
-            } catch (RemoteException e) {
-                throw new IllegalStateException("Package info doesn't exist");
-            }
-
+        public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) {
             // Read the user's key from the ssaid table.
             Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
             if (userKeySetting == null || userKeySetting.isNull()
@@ -2113,11 +2124,12 @@
             }
 
             // Mac the package name and each of the signatures.
-            byte[] packageNameBytes = packageInfo.packageName.getBytes(StandardCharsets.UTF_8);
+            final String packageName = callingPkg.packageName;
+            byte[] packageNameBytes = packageName.getBytes(StandardCharsets.UTF_8);
             m.update(getLengthPrefix(packageNameBytes), 0, 4);
             m.update(packageNameBytes);
-            for (int i = 0; i < packageInfo.signatures.length; i++) {
-                byte[] sig = packageInfo.signatures[i].toByteArray();
+            for (int i = 0; i < callingPkg.signatures.length; i++) {
+                byte[] sig = callingPkg.signatures[i].toByteArray();
                 m.update(getLengthPrefix(sig), 0, 4);
                 m.update(sig);
             }
@@ -2127,7 +2139,7 @@
                     .toLowerCase(Locale.US);
 
             // Save the ssaid in the ssaid table.
-            final String uid = Integer.toString(packageInfo.applicationInfo.uid);
+            final String uid = Integer.toString(callingPkg.applicationInfo.uid);
             final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
             final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true,
                     packageName);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index fbd9f0c..4e7cf72 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -87,7 +87,9 @@
 
     private boolean mMenuVisible;
     private boolean mAllowMenuTimeout = true;
+
     private final List<RemoteAction> mActions = new ArrayList<>();
+
     private View mViewRoot;
     private Drawable mBackgroundDrawable;
     private View mMenuContainer;
@@ -266,7 +268,6 @@
             }
             notifyMenuVisibility(true);
             updateExpandButtonFromBounds(stackBounds, movementBounds);
-            setDecorViewVisibility(true);
             mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
                     mMenuContainer.getAlpha(), 1f);
             mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
@@ -311,11 +312,15 @@
                     if (animationFinishedRunnable != null) {
                         animationFinishedRunnable.run();
                     }
-                    setDecorViewVisibility(false);
+
+                    finish();
                 }
             });
             mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
             mMenuContainerAnimator.start();
+        } else {
+            // If the menu is not visible, just finish now
+            finish();
         }
     }
 
@@ -431,7 +436,6 @@
             alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
         }
         mBackgroundDrawable.setAlpha(alpha);
-        setDecorViewVisibility(alpha > 0);
     }
 
     private void notifyRegisterInputConsumer() {
@@ -508,16 +512,4 @@
         v.removeCallbacks(mFinishRunnable);
         v.postDelayed(mFinishRunnable, delay);
     }
-
-    /**
-     * Sets the visibility of the root view of the window to disable drawing and touches for the
-     * activity.  This differs from {@link Activity#setVisible(boolean)} in that it does not set
-     * the internal mVisibleFromClient state.
-     */
-    private void setDecorViewVisibility(boolean visible) {
-        final View decorView = getWindow().getDecorView();
-        if (decorView != null) {
-            decorView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index bcaa395..875fb14 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -50,6 +50,7 @@
 public class PipMenuActivityController {
 
     private static final String TAG = "PipMenuActController";
+    private static final boolean DEBUG = false;
 
     public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
     public static final String EXTRA_ACTIONS = "actions";
@@ -195,6 +196,10 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     public void setDismissFraction(float fraction) {
+        if (DEBUG) {
+            Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null)
+                    + " fraction=" + fraction);
+        }
         if (mToActivityMessenger != null) {
             mTmpDismissFractionData.clear();
             mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction);
@@ -216,6 +221,9 @@
      * Shows the menu activity.
      */
     public void showMenu(Rect stackBounds, Rect movementBounds, boolean allowMenuTimeout) {
+        if (DEBUG) {
+            Log.d(TAG, "showMenu() hasActivity=" + (mToActivityMessenger != null));
+        }
         if (mToActivityMessenger != null) {
             Bundle data = new Bundle();
             data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
@@ -238,6 +246,9 @@
      * Pokes the menu, indicating that the user is interacting with it.
      */
     public void pokeMenu() {
+        if (DEBUG) {
+            Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null));
+        }
         if (mToActivityMessenger != null) {
             Message m = Message.obtain();
             m.what = PipMenuActivity.MESSAGE_POKE_MENU;
@@ -253,6 +264,9 @@
      * Hides the menu activity.
      */
     public void hideMenu() {
+        if (DEBUG) {
+            Log.d(TAG, "hideMenu() hasActivity=" + (mToActivityMessenger != null));
+        }
         if (mToActivityMessenger != null) {
             Message m = Message.obtain();
             m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
@@ -365,6 +379,10 @@
      * Handles changes in menu visibility.
      */
     private void onMenuVisibilityChanged(boolean visible, boolean resize) {
+        if (DEBUG) {
+            Log.d(TAG, "onMenuVisibilityChanged() mMenuVisible=" + mMenuVisible
+                    + " menuVisible=" + visible + " resize=" + resize);
+        }
         if (visible) {
             mInputConsumerController.unregisterInputConsumer();
         } else {
@@ -389,6 +407,7 @@
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
         pw.println(innerPrefix + "mMenuVisible=" + mMenuVisible);
+        pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger);
         pw.println(innerPrefix + "mListeners=" + mListeners.size());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index a14a712..fb8574d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -23,6 +23,7 @@
 import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
@@ -253,7 +254,7 @@
      * Flings the PiP to the closest snap target.
      */
     Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds,
-            AnimatorUpdateListener listener) {
+            AnimatorUpdateListener updateListener, AnimatorListener listener) {
         cancelAnimations();
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                 velocityX, velocityY);
@@ -263,8 +264,11 @@
             mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                     distanceBetweenRectOffsets(mBounds, toBounds),
                     velocity);
-            if (listener != null) {
-                mBoundsAnimator.addUpdateListener(listener);
+            if (updateListener != null) {
+                mBoundsAnimator.addUpdateListener(updateListener);
+            }
+            if (listener != null){
+                mBoundsAnimator.addListener(listener);
             }
             mBoundsAnimator.start();
         }
@@ -274,14 +278,18 @@
     /**
      * Animates the PiP to the closest snap target.
      */
-    Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener listener) {
+    Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener updateListener,
+            AnimatorListener listener) {
         cancelAnimations();
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
                     FAST_OUT_SLOW_IN, mUpdateBoundsListener);
-            if (listener != null) {
-                mBoundsAnimator.addUpdateListener(listener);
+            if (updateListener != null) {
+                mBoundsAnimator.addUpdateListener(updateListener);
+            }
+            if (listener != null){
+                mBoundsAnimator.addListener(listener);
             }
             mBoundsAnimator.start();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index d68836c..161bdac 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.pip.phone;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.IActivityManager;
@@ -391,7 +393,10 @@
                 final float distance = bounds.bottom - target;
                 fraction = Math.min(distance / bounds.height(), 1f);
             }
-            mMenuController.setDismissFraction(fraction);
+            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
+                // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
+                mMenuController.setDismissFraction(fraction);
+            }
         }
     }
 
@@ -611,22 +616,34 @@
                     setMinimizedStateInternal(false);
                 }
 
-                // If the menu is still visible, and we aren't minimized, then just poke the menu
-                // so that it will timeout after the user stops touching it
+                AnimatorListenerAdapter postAnimationCallback = null;
                 if (mMenuController.isMenuVisible()) {
+                    // If the menu is still visible, and we aren't minimized, then just poke the
+                    // menu so that it will timeout after the user stops touching it
                     mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds,
                             true /* allowMenuTimeout */);
+                } else {
+                    // If the menu is not visible, then we can still be showing the activity for the
+                    // dismiss overlay, so just finish it after the animation completes
+                    postAnimationCallback = new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mMenuController.hideMenu();
+                        }
+                    };
                 }
 
                 if (isFling) {
                     mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
-                            mUpdateScrimListener);
+                            mUpdateScrimListener, postAnimationCallback);
                 } else {
-                    mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener);
+                    mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener,
+                            postAnimationCallback);
                 }
             } else if (mIsMinimized) {
                 // This was a tap, so no longer minimized
-                mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* listener */);
+                mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */,
+                        null /* animatorListener */);
                 setMinimizedStateInternal(false);
             } else if (!mIsMenuVisible) {
                 mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 9efe224..d2a2919 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -242,7 +242,7 @@
         mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
         mDetailSettingsButton.setOnClickListener(v -> {
             Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS,
-                    mDetailAdapter.getMetricsCategory());
+                    adapter.getMetricsCategory());
             Dependency.get(ActivityStarter.class)
                     .postStartActivityDismissingKeyguard(settingsIntent, 0);
         });
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index dc666e9..311f8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -524,7 +524,7 @@
      * changes.
      */
     public void onTaskDataLoaded() {
-        if (mTask.icon != null) {
+        if (mTask != null && mTask.icon != null) {
             mIconView.setImageDrawable(mTask.icon);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 21a0dc9..8298f35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -228,6 +228,7 @@
         // Settings button.
         final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
         if (mAppUid >= 0 && onSettingsClick != null) {
+            settingsButton.setVisibility(View.VISIBLE);
             final int appUidF = mAppUid;
             settingsButton.setOnClickListener(
                     (View view) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index 5055dda..bb82b60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -120,6 +120,9 @@
     @Override
     public void createMenu(ViewGroup parent) {
         mParent = (ExpandableNotificationRow) parent;
+        if (mMenuContainer != null) {
+            mMenuContainer.removeAllViews();
+        }
         mMenuContainer = new FrameLayout(mContext);
         for (int i = 0; i < mMenuItems.size(); i++) {
             addMenuView(mMenuItems.get(i), mMenuContainer);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index a0f2891..715dc82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -23,7 +23,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.internal.widget.CachingIconView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
@@ -127,12 +126,8 @@
         super.setDark(dark, fade, delay);
         if (mDark == dark) return;
         mDark = dark;
-        if (fade) {
-            mViewInvertHelper.fade(dark, delay);
-        } else {
-            mViewInvertHelper.update(dark);
-        }
-        mShelfIcons.setAmbient(dark);
+        mShelfIcons.setDark(dark, fade, delay);
+        updateInteractiveness();
     }
 
     @Override
@@ -439,7 +434,8 @@
                 iconState.scaleY = 1.0f;
                 iconState.hidden = false;
             }
-            if (row.isAboveShelf() || (!row.isInShelf() && isLastChild && row.areGutsExposed())) {
+            if (row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
+                    || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
                 iconState.hidden = true;
             }
             int shelfColor = icon.getStaticDrawableColor();
@@ -576,7 +572,8 @@
     }
 
     private void updateInteractiveness() {
-        mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf;
+        mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf
+                && !mDark;
         setClickable(mInteractive);
         setFocusable(mInteractive);
         setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index b9c8a78..92bfae9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,6 +27,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -46,6 +47,7 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 
 import java.text.NumberFormat;
@@ -99,7 +101,6 @@
     private int mDensity;
     private float mIconScale = 1.0f;
     private final Paint mDotPaint = new Paint();
-    private boolean mDotVisible;
     private float mDotRadius;
     private int mStaticDotRadius;
     private int mVisibleState = STATE_ICON;
@@ -110,6 +111,8 @@
     private OnVisibilityChangedListener mOnVisibilityChangedListener;
     private int mDrawableColor;
     private int mIconColor;
+    private int mDecorColor;
+    private float mDarkAmount;
     private ValueAnimator mColorAnimator;
     private int mCurrentSetColor = NO_COLOR;
     private int mAnimationStartColor = NO_COLOR;
@@ -119,6 +122,7 @@
                 animation.getAnimatedFraction());
         setColorInternal(newColor);
     };
+    private final NotificationIconDozeHelper mDozer;
 
     public StatusBarIconView(Context context, String slot, Notification notification) {
         this(context, slot, notification, false);
@@ -127,6 +131,7 @@
     public StatusBarIconView(Context context, String slot, Notification notification,
             boolean blocked) {
         super(context);
+        mDozer = new NotificationIconDozeHelper(context);
         mBlocked = blocked;
         mSlot = slot;
         mNumberPain = new Paint();
@@ -190,6 +195,7 @@
 
     public StatusBarIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mDozer = new NotificationIconDozeHelper(context);
         mBlocked = false;
         mAlwaysScaleIcon = true;
         updateIconScale();
@@ -466,7 +472,19 @@
      * to the drawable.
      */
     public void setDecorColor(int iconTint) {
-        mDotPaint.setColor(iconTint);
+        mDecorColor = iconTint;
+        updateDecorColor();
+    }
+
+    private void updateDecorColor() {
+        int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount);
+        if (mDotPaint.getColor() != color) {
+            mDotPaint.setColor(color);
+
+            if (mDotAppearAmount != 0) {
+                invalidate();
+            }
+        }
     }
 
     /**
@@ -477,6 +495,7 @@
         mDrawableColor = color;
         setColorInternal(color);
         mIconColor = color;
+        mDozer.setColor(color);
     }
 
     private void setColorInternal(int color) {
@@ -649,6 +668,14 @@
         mOnVisibilityChangedListener = listener;
     }
 
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mDozer.setImageDark(this, dark, fade, delay, mIconColor == NO_COLOR);
+        mDozer.setIntensityDark(f -> {
+            mDarkAmount = f;
+            updateDecorColor();
+        }, dark, fade, delay);
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 3efa29f..bca4b43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -18,7 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.content.Context;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.view.View;
@@ -38,8 +38,8 @@
     private boolean mIsLegacy;
     private int mLegacyColor;
 
-    protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) {
-        super(view, row);
+    protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+        super(ctx, view, row);
         mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
         mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color);
     }
@@ -67,13 +67,11 @@
     }
 
     protected void fadeGrayscale(final boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateGrayscaleMatrix((float) animation.getAnimatedValue());
-                mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-                mView.setLayerPaint(mGreyPaint);
-            }
+        getDozer().startIntensityAnimation(animation -> {
+            getDozer().updateGrayscaleMatrix((float) animation.getAnimatedValue());
+            mGreyPaint.setColorFilter(
+                    new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix()));
+            mView.setLayerPaint(mGreyPaint);
         }, dark, delay, new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -86,9 +84,9 @@
 
     protected void updateGrayscale(boolean dark) {
         if (dark) {
-            updateGrayscaleMatrix(1f);
+            getDozer().updateGrayscaleMatrix(1f);
             mGreyPaint.setColorFilter(
-                    new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+                    new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix()));
             mView.setLayerPaint(mGreyPaint);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
new file mode 100644
index 0000000..d592c5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.statusbar.notification;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.widget.ImageView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+import java.util.function.Consumer;
+
+public class NotificationDozeHelper {
+    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+
+    public void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateGrayscaleMatrix((float) animation.getAnimatedValue());
+                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+            }
+        }, dark, delay, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!dark) {
+                    target.setColorFilter(null);
+                }
+            }
+        });
+    }
+
+    public void updateGrayscale(ImageView target, boolean dark) {
+        if (dark) {
+            updateGrayscaleMatrix(1f);
+            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+        } else {
+            target.setColorFilter(null);
+        }
+    }
+
+    public void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+            boolean dark, long delay, Animator.AnimatorListener listener) {
+        float startIntensity = dark ? 0f : 1f;
+        float endIntensity = dark ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(updateListener);
+        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        animator.setStartDelay(delay);
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        animator.start();
+    }
+
+    public void setIntensityDark(Consumer<Float> listener, boolean dark,
+            boolean animate, long delay) {
+        if (animate) {
+            startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dark, delay,
+                    null /* listener */);
+        } else {
+            listener.accept(dark ? 1f : 0f);
+        }
+    }
+
+    public void updateGrayscaleMatrix(float intensity) {
+        mGrayscaleColorMatrix.setSaturation(1 - intensity);
+    }
+
+    public ColorMatrix getGrayscaleColorMatrix() {
+        return mGrayscaleColorMatrix;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 38e4ec1..1ffc944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -16,17 +16,10 @@
 
 package com.android.systemui.statusbar.notification;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.app.Notification;
 import android.content.Context;
-import android.graphics.Color;
 import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
 import android.util.ArraySet;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -37,7 +30,6 @@
 import android.widget.TextView;
 
 import com.android.systemui.Interpolators;
-import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.TransformableView;
@@ -55,10 +47,6 @@
 
     private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
             = new PathInterpolator(0.4f, 0f, 0.7f, 1f);
-    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
-            0, PorterDuff.Mode.SRC_ATOP);
-    private final int mIconDarkAlpha;
-    private final int mIconDarkColor = 0xffffffff;
 
     protected final ViewInvertHelper mInvertHelper;
     protected final ViewTransformationHelper mTransformationHelper;
@@ -74,8 +62,7 @@
     private boolean mTransformLowPriorityTitle;
 
     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
-        super(view, row);
-        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+        super(ctx, view, row);
         mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
         mTransformationHelper = new ViewTransformationHelper();
 
@@ -108,6 +95,16 @@
         updateInvertHelper();
     }
 
+    @Override
+    protected NotificationDozeHelper createDozer(Context ctx) {
+        return new NotificationIconDozeHelper(ctx);
+    }
+
+    @Override
+    protected NotificationIconDozeHelper getDozer() {
+        return (NotificationIconDozeHelper) super.getDozer();
+    }
+
     protected void resolveHeaderViews() {
         mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
         mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text);
@@ -116,6 +113,7 @@
         mColor = resolveColor(mExpandButton);
         mNotificationHeader = (NotificationHeaderView) mView.findViewById(
                 com.android.internal.R.id.notification_header);
+        getDozer().setColor(mColor);
     }
 
     private int resolveColor(ImageView icon) {
@@ -223,90 +221,8 @@
             // It also may lead to bugs where the icon isn't correctly greyed out.
             boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
                     != NotificationHeaderView.NO_COLOR;
-            if (fade) {
-                if (hadColorFilter) {
-                    fadeIconColorFilter(mIcon, dark, delay);
-                    fadeIconAlpha(mIcon, dark, delay);
-                } else {
-                    fadeGrayscale(mIcon, dark, delay);
-                }
-            } else {
-                if (hadColorFilter) {
-                    updateIconColorFilter(mIcon, dark);
-                    updateIconAlpha(mIcon, dark);
-                } else {
-                    updateGrayscale(mIcon, dark);
-                }
-            }
-        }
-    }
 
-    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
-            }
-        }, dark, delay, null /* listener */);
-    }
-
-    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float t = (float) animation.getAnimatedValue();
-                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
-            }
-        }, dark, delay, null /* listener */);
-    }
-
-    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateGrayscaleMatrix((float) animation.getAnimatedValue());
-                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-            }
-        }, dark, delay, new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!dark) {
-                    target.setColorFilter(null);
-                }
-            }
-        });
-    }
-
-    private void updateIconColorFilter(ImageView target, boolean dark) {
-        updateIconColorFilter(target, dark ? 1f : 0f);
-    }
-
-    private void updateIconColorFilter(ImageView target, float intensity) {
-        int color = interpolateColor(mColor, mIconDarkColor, intensity);
-        mIconColorFilter.setColor(color);
-        Drawable iconDrawable = target.getDrawable();
-
-        // Also, the notification might have been modified during the animation, so background
-        // might be null here.
-        if (iconDrawable != null) {
-            Drawable d = iconDrawable.mutate();
-            // DrawableContainer ignores the color filter if it's already set, so clear it first to
-            // get it set and invalidated properly.
-            d.setColorFilter(null);
-            d.setColorFilter(mIconColorFilter);
-        }
-    }
-
-    private void updateIconAlpha(ImageView target, boolean dark) {
-        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
-    }
-
-    protected void updateGrayscale(ImageView target, boolean dark) {
-        if (dark) {
-            updateGrayscaleMatrix(1f);
-            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-        } else {
-            target.setColorFilter(null);
+            getDozer().setImageDark(mIcon, dark, fade, delay, !hadColorFilter);
         }
     }
 
@@ -316,22 +232,6 @@
         mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
     }
 
-    private static int interpolateColor(int source, int target, float t) {
-        int aSource = Color.alpha(source);
-        int rSource = Color.red(source);
-        int gSource = Color.green(source);
-        int bSource = Color.blue(source);
-        int aTarget = Color.alpha(target);
-        int rTarget = Color.red(target);
-        int gTarget = Color.green(target);
-        int bTarget = Color.blue(target);
-        return Color.argb(
-                (int) (aSource * (1f - t) + aTarget * t),
-                (int) (rSource * (1f - t) + rTarget * t),
-                (int) (gSource * (1f - t) + gTarget * t),
-                (int) (bSource * (1f - t) + bTarget * t));
-    }
-
     @Override
     public NotificationHeaderView getNotificationHeader() {
         return mNotificationHeader;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
new file mode 100644
index 0000000..9f79ef2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
@@ -0,0 +1,101 @@
+/*
+ * 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.statusbar.notification;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class NotificationIconDozeHelper extends NotificationDozeHelper {
+
+    private final int mImageDarkAlpha;
+    private final int mImageDarkColor = 0xffffffff;
+    private final PorterDuffColorFilter mImageColorFilter = new PorterDuffColorFilter(
+            0, PorterDuff.Mode.SRC_ATOP);
+
+    private int mColor = Color.BLACK;
+
+    public NotificationIconDozeHelper(Context ctx) {
+        mImageDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+    }
+
+    public void setColor(int color) {
+        mColor = color;
+    }
+
+    public void setImageDark(ImageView target, boolean dark, boolean fade, long delay,
+            boolean useGrayscale) {
+        if (fade) {
+            if (!useGrayscale) {
+                fadeImageColorFilter(target, dark, delay);
+                fadeImageAlpha(target, dark, delay);
+            } else {
+                fadeGrayscale(target, dark, delay);
+            }
+        } else {
+            if (!useGrayscale) {
+                updateImageColorFilter(target, dark);
+                updateImageAlpha(target, dark);
+            } else {
+                updateGrayscale(target, dark);
+            }
+        }
+    }
+
+    private void fadeImageColorFilter(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(animation -> {
+            updateImageColorFilter(target, (Float) animation.getAnimatedValue());
+        }, dark, delay, null /* listener */);
+    }
+
+    private void fadeImageAlpha(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(animation -> {
+            float t = (float) animation.getAnimatedValue();
+            target.setImageAlpha((int) (255 * (1f - t) + mImageDarkAlpha * t));
+        }, dark, delay, null /* listener */);
+    }
+
+    private void updateImageColorFilter(ImageView target, boolean dark) {
+        updateImageColorFilter(target, dark ? 1f : 0f);
+    }
+
+    private void updateImageColorFilter(ImageView target, float intensity) {
+        int color = NotificationUtils.interpolateColors(mColor, mImageDarkColor, intensity);
+        mImageColorFilter.setColor(color);
+        Drawable imageDrawable = target.getDrawable();
+
+        // Also, the notification might have been modified during the animation, so background
+        // might be null here.
+        if (imageDrawable != null) {
+            Drawable d = imageDrawable.mutate();
+            // DrawableContainer ignores the color filter if it's already set, so clear it first to
+            // get it set and invalidated properly.
+            d.setColorFilter(null);
+            d.setColorFilter(mImageColorFilter);
+        }
+    }
+
+    private void updateImageAlpha(ImageView target, boolean dark) {
+        target.setImageAlpha(dark ? mImageDarkAlpha : 255);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 846d03a..f0b6b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification;
 
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
 import android.service.notification.StatusBarNotification;
@@ -46,7 +45,8 @@
     private int mContentHeight;
     private int mMinHeightHint;
 
-    protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+    protected NotificationTemplateViewWrapper(Context ctx, View view,
+            ExpandableNotificationRow row) {
         super(ctx, view, row);
         mTransformationHelper.setCustomTransformation(
                 new ViewTransformationHelper.CustomTransformation() {
@@ -154,16 +154,20 @@
         // This also clears the existing types
         super.updateTransformedTypes();
         if (mTitle != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle);
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
+                    mTitle);
         }
         if (mText != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText);
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+                    mText);
         }
         if (mPicture != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture);
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
+                    mPicture);
         }
         if (mProgressBar != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar);
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
+                    mProgressBar);
         }
     }
 
@@ -173,7 +177,7 @@
             return;
         }
         super.setDark(dark, fade, delay);
-        setPictureGrayscale(dark, fade, delay);
+        setPictureDark(dark, fade, delay);
         setProgressBarDark(dark, fade, delay);
     }
 
@@ -188,12 +192,9 @@
     }
 
     private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float t = (float) animation.getAnimatedValue();
-                updateProgressDark(target, t);
-            }
+        getDozer().startIntensityAnimation(animation -> {
+            float t = (float) animation.getAnimatedValue();
+            updateProgressDark(target, t);
         }, dark, delay, null /* listener */);
     }
 
@@ -207,13 +208,9 @@
         updateProgressDark(target, dark ? 1f : 0f);
     }
 
-    protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
+    private void setPictureDark(boolean dark, boolean fade, long delay) {
         if (mPicture != null) {
-            if (fade) {
-                fadeGrayscale(mPicture, grayscale, delay);
-            } else {
-                updateGrayscale(mPicture, grayscale);
-            }
+            getDozer().setImageDark(mPicture, dark, fade, delay, true /* useGrayscale */);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index c85e8d8..f4db9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -16,24 +16,17 @@
 
 package com.android.systemui.statusbar.notification;
 
-import android.animation.Animator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
 import android.support.v4.graphics.ColorUtils;
 import android.view.NotificationHeaderView;
 import android.view.View;
 
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * Wraps the actual notification content view; used to implement behaviors which are different for
@@ -41,14 +34,14 @@
  */
 public abstract class NotificationViewWrapper implements TransformableView {
 
-    protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
     protected final View mView;
     protected final ExpandableNotificationRow mRow;
+    private final NotificationDozeHelper mDozer;
+
     protected boolean mDark;
     private int mBackgroundColor = 0;
     protected boolean mShouldInvertDark;
     protected boolean mDarkInitialized = false;
-    private boolean mForcedInvisible;
 
     public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -65,13 +58,22 @@
         } else if (v instanceof NotificationHeaderView) {
             return new NotificationHeaderViewWrapper(ctx, v, row);
         } else {
-            return new NotificationCustomViewWrapper(v, row);
+            return new NotificationCustomViewWrapper(ctx, v, row);
         }
     }
 
-    protected NotificationViewWrapper(View view, ExpandableNotificationRow row) {
+    protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
         mView = view;
         mRow = row;
+        mDozer = createDozer(ctx);
+    }
+
+    protected NotificationDozeHelper createDozer(Context ctx) {
+        return new NotificationDozeHelper();
+    }
+
+    protected NotificationDozeHelper getDozer() {
+        return mDozer;
     }
 
     /**
@@ -112,26 +114,6 @@
                 || ColorUtils.calculateLuminance(backgroundColor) > 0.5;
     }
 
-
-    protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
-            boolean dark, long delay, Animator.AnimatorListener listener) {
-        float startIntensity = dark ? 0f : 1f;
-        float endIntensity = dark ? 1f : 0f;
-        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
-        animator.addUpdateListener(updateListener);
-        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
-        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        animator.setStartDelay(delay);
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        animator.start();
-    }
-
-    protected void updateGrayscaleMatrix(float intensity) {
-        mGrayscaleColorMatrix.setSaturation(1 - intensity);
-    }
-
     /**
      * Update the appearance of the expand button.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 3706dc8..dee15d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -95,7 +95,7 @@
     private int mActualLayoutWidth = NO_VALUE;
     private float mActualPaddingEnd = NO_VALUE;
     private float mActualPaddingStart = NO_VALUE;
-    private boolean mCentered;
+    private boolean mDark;
     private boolean mChangingViewPositions;
     private int mAddAnimationStartIndex = -1;
     private int mCannedAnimationStartIndex = -1;
@@ -183,6 +183,9 @@
                 mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
             }
         }
+        if (mDark && child instanceof StatusBarIconView) {
+            ((StatusBarIconView) child).setDark(mDark, false, 0);
+        }
     }
 
     @Override
@@ -312,7 +315,8 @@
                 numDots++;
             }
         }
-        if (mCentered && translationX < getLayoutEnd()) {
+        boolean center = mDark;
+        if (center && translationX < getLayoutEnd()) {
             float delta = (getLayoutEnd() - translationX) / 2;
             for (int i = 0; i < childCount; i++) {
                 View view = getChildAt(i);
@@ -390,9 +394,15 @@
         mChangingViewPositions = changingViewPositions;
     }
 
-    public void setAmbient(boolean ambient) {
-        mCentered = ambient;
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mDark = dark;
         mDisallowNextAnimation = true;
+        for (int i = 0; i < getChildCount(); i++) {
+            View view = getChildAt(i);
+            if (view instanceof StatusBarIconView) {
+                ((StatusBarIconView) view).setDark(dark, fade, delay);
+            }
+        }
     }
 
     public IconState getIconState(StatusBarIconView icon) {
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 9dc0ceb..472af65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5010,23 +5010,25 @@
                 @Override
                 public void onPulseStarted() {
                     callback.onPulseStarted();
-                    if (!mHeadsUpManager.getAllEntries().isEmpty()) {
+                    Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
+                            mHeadsUpManager.getAllEntries();
+                    if (!pulsingEntries.isEmpty()) {
                         // Only pulse the stack scroller if there's actually something to show.
                         // Otherwise just show the always-on screen.
-                        setPulsing(true);
+                        setPulsing(pulsingEntries);
                     }
                 }
 
                 @Override
                 public void onPulseFinished() {
                     callback.onPulseFinished();
-                    setPulsing(false);
+                    setPulsing(null);
                 }
 
-                private void setPulsing(boolean pulsing) {
+                private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
                     mStackScroller.setPulsing(pulsing);
-                    mNotificationPanel.setPulsing(pulsing);
-                    mVisualStabilityManager.setPulsing(pulsing);
+                    mNotificationPanel.setPulsing(pulsing != null);
+                    mVisualStabilityManager.setPulsing(pulsing != null);
                 }
             }, reason);
         }
@@ -5769,12 +5771,16 @@
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
             final String pkg = sbn.getPackageName();
             NotificationInfo info = (NotificationInfo) gutsView;
-            final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v,
-                    NotificationChannel channel, int appUid) -> {
-                mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
-                guts.resetFalsingCheck();
-                startAppNotificationSettingsActivity(pkg, appUid, channel);
-            };
+            // Settings link is only valid for notifications that specify a user, unless this is the
+            // system user.
+            NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+            if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) {
+                onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+                    mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
+                    guts.resetFalsingCheck();
+                    startAppNotificationSettingsActivity(pkg, appUid, channel);
+                };
+            }
             final View.OnClickListener onDoneClick = (View v) -> {
                 saveAndCloseNotificationMenu(info, row, guts, v);
             };
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 5bead73..15fcb38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -72,6 +72,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationGuts;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StackScrollerDecorView;
@@ -86,6 +87,7 @@
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
@@ -331,7 +333,7 @@
         }
     };
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
-    private boolean mPulsing;
+    private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
     private boolean mDrawBackgroundAsSrc;
     private boolean mFadingOut;
     private boolean mParentNotFullyVisible;
@@ -1917,15 +1919,19 @@
         int numShownItems = 0;
         boolean finish = false;
         int maxDisplayedNotifications = mAmbientState.isDark()
-                ? (mPulsing ? 1 : 0)
+                ? (isPulsing() ? 1 : 0)
                 : mMaxDisplayedNotifications;
 
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
             if (expandableView.getVisibility() != View.GONE
                     && !expandableView.hasNoContentHeight()) {
-                if (maxDisplayedNotifications != -1
-                        && numShownItems >= maxDisplayedNotifications) {
+                boolean limitReached = maxDisplayedNotifications != -1
+                        && numShownItems >= maxDisplayedNotifications;
+                boolean notificationOnAmbientThatIsNotPulsing = isPulsing()
+                        && expandableView instanceof ExpandableNotificationRow
+                        && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry());
+                if (limitReached || notificationOnAmbientThatIsNotPulsing) {
                     expandableView = mShelf;
                     finish = true;
                 }
@@ -1971,6 +1977,19 @@
         mAmbientState.setLayoutMaxHeight(mContentHeight);
     }
 
+    private boolean isPulsing(NotificationData.Entry entry) {
+        for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
+            if (e.entry == entry) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isPulsing() {
+        return mPulsing != null;
+    }
+
     private void updateScrollability() {
         boolean scrollable = getScrollRange() > 0;
         if (scrollable != mScrollable) {
@@ -2784,7 +2803,7 @@
     }
 
     private void updateNotificationAnimationStates() {
-        boolean running = mAnimationsEnabled || mPulsing;
+        boolean running = mAnimationsEnabled || isPulsing();
         mShelf.setAnimationsEnabled(running);
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2795,7 +2814,7 @@
     }
 
     private void updateAnimationState(View child) {
-        updateAnimationState((mAnimationsEnabled || mPulsing)
+        updateAnimationState((mAnimationsEnabled || isPulsing())
                 && (mIsExpanded || isPinnedHeadsUp(child)), child);
     }
 
@@ -4055,12 +4074,12 @@
         return mIsExpanded;
     }
 
-    public void setPulsing(boolean pulsing) {
-        if (mPulsing == pulsing) {
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+        if (mPulsing == null && pulsing == null) {
             return;
         }
         mPulsing = pulsing;
-        mAmbientState.setPulsing(pulsing);
+        mAmbientState.setPulsing(isPulsing());
         updateNotificationAnimationStates();
         updateContentHeight();
         notifyHeightChangeListener(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index c8659fb..5b594be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -190,7 +190,9 @@
             view.setScaleY(scaleY);
         }
 
-        boolean becomesInvisible = this.alpha == 0.0f || (this.hidden && !isAnimating(view));
+        int oldVisibility = view.getVisibility();
+        boolean becomesInvisible = this.alpha == 0.0f
+                || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
         boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
         if (animatingAlpha) {
             updateAlphaAnimation(view);
@@ -212,7 +214,6 @@
         }
 
         // apply visibility
-        int oldVisibility = view.getVisibility();
         int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
         if (newVisibility != oldVisibility) {
             if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index c67cccc..8609eeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -100,4 +100,10 @@
         ViewUtils.detachView(mQsDetail);
         mTestableLooper.processAllMessages();
     }
+
+    @Test
+    public void testNullAdapterClick() {
+        mQsDetail.setupDetailFooter(mock(DetailAdapter.class));
+        mQsDetail.findViewById(android.R.id.button2).performClick();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 21930a3..0621f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -206,6 +206,29 @@
     }
 
     @Test
+    public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                null, null, null, null);
+        final TextView settingsButton =
+                (TextView) mNotificationInfo.findViewById(R.id.more_settings);
+        assertTrue(settingsButton.getVisibility() != View.VISIBLE);
+    }
+
+    @Test
+    public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                null, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+        final TextView settingsButton =
+                (TextView) mNotificationInfo.findViewById(R.id.more_settings);
+        assertEquals(View.VISIBLE, settingsButton.getVisibility());
+    }
+
+    @Test
     public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
index 31b9bae..efb9fea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
@@ -44,4 +44,11 @@
         ViewUtils.detachView(row.getMenuView());
         TestableLooper.get(this).processAllMessages();
     }
+
+    @Test
+    public void testRecreateMenu() {
+        NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
+        row.createMenu(null);
+        row.createMenu(null);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
new file mode 100644
index 0000000..a69de7a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.statusbar.notification;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class NotificationViewWrapperTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void constructor_doesntUseViewContext() throws Exception {
+        new TestableNotificationViewWrapper(mContext, new View(null /* context */), null /* row */);
+    }
+
+    static class TestableNotificationViewWrapper extends NotificationViewWrapper {
+        protected TestableNotificationViewWrapper(Context ctx, View view,
+                ExpandableNotificationRow row) {
+            super(ctx, view, row);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 0482e73..ac81565 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1631,7 +1631,7 @@
 
     @Override
     public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
-            int profileId) {
+            int profileId, String packageName) {
         final int userId = UserHandle.getCallingUserId();
 
         if (DEBUG) {
@@ -1653,8 +1653,11 @@
                 Provider provider = mProviders.get(i);
                 AppWidgetProviderInfo info = provider.info;
 
-                // Ignore an invalid provider or one not matching the filter.
-                if (provider.zombie || (info.widgetCategory & categoryFilter) == 0) {
+                // Ignore an invalid provider, one not matching the filter,
+                // or one that isn't in the given package, if any.
+                boolean inPackage = packageName == null
+                        || provider.id.componentName.getPackageName().equals(packageName);
+                if (provider.zombie || (info.widgetCategory & categoryFilter) == 0 || !inPackage) {
                     continue;
                 }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 15ec98f..c8a5780 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -148,7 +148,7 @@
                 // TODO(b/33197203): since service is fetching the data (to use for save later),
                 // we should optimize what's sent (for example, remove layout containers,
                 // color / font info, etc...)
-                session.mStructure = structure;
+                session.setStructureLocked(structure);
             }
 
 
@@ -194,13 +194,17 @@
         }
     }
 
+    private String getComponentNameFromSettings() {
+        return Settings.Secure.getStringForUser(
+                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
+    }
+
     void updateLocked(boolean disabled) {
         final boolean wasEnabled = isEnabled();
         mDisabled = disabled;
         ComponentName serviceComponent = null;
         ServiceInfo serviceInfo = null;
-        final String componentName = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
+        final String componentName = getComponentNameFromSettings();
         if (!TextUtils.isEmpty(componentName)) {
             try {
                 serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -413,8 +417,7 @@
     void disableSelf() {
         final long identity = Binder.clearCallingIdentity();
         try {
-            final String autoFillService = Settings.Secure.getStringForUser(
-                    mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
+            final String autoFillService = getComponentNameFromSettings();
             if (mInfo.getServiceInfo().getComponentName().equals(
                     ComponentName.unflattenFromString(autoFillService))) {
                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
@@ -432,12 +435,14 @@
     void dumpLocked(String prefix, PrintWriter pw) {
         final String prefix2 = prefix + "  ";
 
-        pw.print(prefix); pw.print("User :"); pw.println(mUserId);
-        pw.print(prefix); pw.print("Component:"); pw.println(mInfo != null
+        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+        pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
                 ? mInfo.getServiceInfo().getComponentName() : null);
+        pw.print(prefix); pw.print("Component from settings: ");
+            pw.println(getComponentNameFromSettings());
         pw.print(prefix); pw.print("Default component: ");
             pw.println(mContext.getString(R.string.config_defaultAutofillService));
-        pw.print(prefix); pw.print("Disabled:"); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
 
         if (VERBOSE && mInfo != null) {
             // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 801769c..a3da50e 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -29,6 +29,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.AutofillOverlay;
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
 import android.content.ComponentName;
@@ -100,7 +101,7 @@
     @NonNull private final String mPackageName;
 
     @GuardedBy("mLock")
-    private final Map<AutofillId, ViewState> mViewStates = new ArrayMap<>();
+    private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>();
 
     /**
      * Id of the View currently being displayed.
@@ -132,7 +133,8 @@
      * Assist structure sent by the app; it will be updated (sanitized, change values for save)
      * before sent to {@link AutofillService}.
      */
-    @GuardedBy("mLock") AssistStructure mStructure;
+    @GuardedBy("mLock")
+    private AssistStructure mStructure;
 
     /**
      * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
@@ -140,6 +142,13 @@
     private boolean mHasCallback;
 
     /**
+     * Extras sent by service on {@code onFillRequest()} calls; the first non-null extra is saved
+     * and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
+     */
+    @GuardedBy("mLock")
+    private Bundle mExtras;
+
+    /**
      * Flags used to start the session.
      */
     int mFlags;
@@ -352,6 +361,10 @@
         mHasCallback = hasIt;
     }
 
+    public void setStructureLocked(AssistStructure structure) {
+        mStructure = structure;
+    }
+
     /**
      * Shows the save UI, when session can be saved.
      *
@@ -474,9 +487,6 @@
             Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
         }
 
-        // TODO(b/33197203 , b/35707731): decide how to handle bundle in multiple partitions
-        final Bundle extras = mResponses != null ? mResponses.get(0).getExtras() : null;
-
         for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
             final AutofillValue value = entry.getValue().getCurrentValue();
             if (value == null) {
@@ -506,7 +516,7 @@
             mStructure.dump();
         }
 
-        mRemoteFillService.onSaveRequest(mStructure, extras);
+        mRemoteFillService.onSaveRequest(mStructure, mExtras);
     }
 
     void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
@@ -517,10 +527,10 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Creating viewState for " + id + " on " + getFlagAsString(flags));
                 }
-                viewState = new ViewState(this, id, this, ViewState.STATE_INITIAL);
+                viewState = new ViewState(this, id, value, this, ViewState.STATE_INITIAL);
                 mViewStates.put(id, viewState);
             } else if ((flags & FLAG_VIEW_ENTERED) != 0) {
-                viewState = startPartitionLocked(id);
+                viewState = startPartitionLocked(id, value);
             } else {
                 if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id);
                 return;
@@ -584,25 +594,38 @@
         Slog.w(TAG, "updateLocked(): unknown flags " + flags + ": " + getFlagAsString(flags));
     }
 
-    private ViewState startPartitionLocked(AutofillId id) {
+    private ViewState startPartitionLocked(AutofillId id, AutofillValue value) {
         if (DEBUG) {
             Slog.d(TAG, "Starting partition for view id " + id);
         }
-        final ViewState viewState =
-                new ViewState(this, id, this,ViewState.STATE_STARTED_PARTITION);
-        mViewStates.put(id, viewState);
+        final ViewState newViewState =
+                new ViewState(this, id, value, this,ViewState.STATE_STARTED_PARTITION);
+        mViewStates.put(id, newViewState);
 
-        /*
-         * TODO(b/33197203 , b/35707731): when start a new partition, it should
-         *
-         * - add autofilled fields as sanitized
-         * - set focus on ViewStructure that triggered it
-         * - pass the first onFillRequest() bundle
-         * - optional: perhaps add a new flag onFilLRequest() to indicate it's a new partition?
-         */
-        mRemoteFillService.onFillRequest(mStructure, null, 0);
+        // Must update value of nodes so:
+        // - proper node is focused
+        // - autofillValue is sent back to service when it was previously autofilled
+        for (int i = 0; i < mViewStates.size(); i++) {
+            final ViewState viewState = mViewStates.valueAt(i);
 
-        return viewState;
+            final ViewNode node = findViewNodeByIdLocked(viewState.id);
+            if (node == null) {
+                Slog.w(TAG, "startPartitionLocked(): no node for " + viewState.id);
+                continue;
+            }
+
+            final AutofillValue initialValue = viewState.getInitialValue();
+            final AutofillValue filledValue = viewState.getAutofilledValue();
+            final AutofillOverlay overlay = new AutofillOverlay();
+            if (filledValue != null && !filledValue.equals(initialValue)) {
+                overlay.value = filledValue;
+            }
+            overlay.focused = id.equals(viewState.id);
+            node.setAutofillOverlay(overlay);
+        }
+        mRemoteFillService.onFillRequest(mStructure, mExtras, 0);
+
+        return newViewState;
     }
 
     @Override
@@ -650,6 +673,9 @@
             mResponses = new ArrayList<>(4);
         }
         mResponses.add(response);
+        if (response != null) {
+            mExtras = response.getExtras();
+        }
 
         setViewStatesLocked(response, ViewState.STATE_FILLABLE);
 
@@ -695,7 +721,7 @@
             if (viewState != null)  {
                 viewState.setState(state);
             } else {
-                viewState = new ViewState(this, id, this, state);
+                viewState = new ViewState(this, id, null, this, state);
                 if (DEBUG) { // TODO(b/33197203): change to VERBOSE once stable
                     Slog.d(TAG, "Adding autofillable view with id " + id + " and state " + state);
                 }
@@ -791,6 +817,7 @@
             }
         }
         pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
+        pw.print(prefix); pw.print("mExtras: "); pw.println(Helper.bundleToString(mExtras));
         mRemoteFillService.dump(prefix, pw);
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 549f231..f8919ee 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -67,15 +67,17 @@
     private final Session mSession;
     private FillResponse mResponse;
 
+    private AutofillValue mInitialValue;
     private AutofillValue mCurrentValue;
     private AutofillValue mAutofilledValue;
     private Rect mVirtualBounds;
 
     private int mState;
 
-    ViewState(Session session, AutofillId id, Listener listener, int state) {
+    ViewState(Session session, AutofillId id, AutofillValue value, Listener listener, int state) {
         mSession = session;
         this.id = id;
+        mInitialValue = value;
         mListener = listener;
         mState = state;
     }
@@ -110,6 +112,11 @@
     }
 
     @Nullable
+    AutofillValue getInitialValue() {
+        return mInitialValue;
+    }
+
+    @Nullable
     FillResponse getResponse() {
         return mResponse;
     }
@@ -184,14 +191,16 @@
 
     @Override
     public String toString() {
-        return "ViewState: [id=" + id + ", currentValue=" + mCurrentValue
-                + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() +"]";
+        return "ViewState: [id=" + id + ", initialValue=" + mInitialValue
+                + ", currentValue=" + mCurrentValue + ", autofilledValue=" + mAutofilledValue
+                + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]";
     }
 
     void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("id:" ); pw.println(this.id);
         pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
         pw.print(prefix); pw.print("has response:" ); pw.println(mResponse != null);
+        pw.print(prefix); pw.print("initialValue:" ); pw.println(mInitialValue);
         pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
         pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
         pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 509351b..2c08afa 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -117,6 +117,12 @@
             case SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD:
                 type = context.getString(R.string.autofill_save_type_credit_card);
                 break;
+            case SaveInfo.SAVE_DATA_TYPE_USERNAME:
+                type = context.getString(R.string.autofill_save_type_username);
+                break;
+            case SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS:
+                type = context.getString(R.string.autofill_save_type_email_address);
+                break;
             default:
                 type = null;
         }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
new file mode 100644
index 0000000..a7ce95b
--- /dev/null
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -0,0 +1,494 @@
+/*
+ * 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;
+
+import static android.Manifest.permission.DUMP;
+import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static android.net.IpSecManager.KEY_RESOURCE_ID;
+import static android.net.IpSecManager.KEY_SPI;
+import static android.net.IpSecManager.KEY_STATUS;
+
+import android.content.Context;
+import android.net.IIpSecService;
+import android.net.INetd;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecConfig;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.util.NetdService;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** @hide */
+public class IpSecService extends IIpSecService.Stub {
+    private static final String TAG = "IpSecService";
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String NETD_SERVICE_NAME = "netd";
+    private static final int[] DIRECTIONS =
+            new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
+
+    /** Binder context for this service */
+    private final Context mContext;
+
+    private Object mLock = new Object();
+
+    private static final int NETD_FETCH_TIMEOUT = 5000; //ms
+
+    private AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0);
+
+    private abstract class ManagedResource implements IBinder.DeathRecipient {
+        final int pid;
+        final int uid;
+        private IBinder mBinder;
+
+        ManagedResource(IBinder binder) {
+            super();
+            mBinder = binder;
+            pid = Binder.getCallingPid();
+            uid = Binder.getCallingUid();
+
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+
+        /**
+         * When this record is no longer needed for managing system resources this function should
+         * unlink all references held by the record to allow efficient garbage collection.
+         */
+        public final void release() {
+            //Release all the underlying system resources first
+            releaseResources();
+
+            if (mBinder != null) {
+                mBinder.unlinkToDeath(this, 0);
+            }
+            mBinder = null;
+
+            //remove this record so that it can be cleaned up
+            nullifyRecord();
+        }
+
+        /**
+         * If the Binder object dies, this function is called to free the system resources that are
+         * being managed by this record and to subsequently release this record for garbage
+         * collection
+         */
+        public final void binderDied() {
+            release();
+        }
+
+        /**
+         * Implement this method to release all object references contained in the subclass to allow
+         * efficient garbage collection of the record. This should remove any references to the
+         * record from all other locations that hold a reference as the record is no longer valid.
+         */
+        protected abstract void nullifyRecord();
+
+        /**
+         * Implement this method to release all system resources that are being protected by this
+         * record. Once the resources are released, the record should be invalidated and no longer
+         * used by calling releaseRecord()
+         */
+        protected abstract void releaseResources();
+    };
+
+    private final class TransformRecord extends ManagedResource {
+        private IpSecConfig mConfig;
+        private int mResourceId;
+
+        TransformRecord(IpSecConfig config, int resourceId, IBinder binder) {
+            super(binder);
+            mConfig = config;
+            mResourceId = resourceId;
+        }
+
+        public IpSecConfig getConfig() {
+            return mConfig;
+        }
+
+        @Override
+        protected void releaseResources() {
+            for (int direction : DIRECTIONS) {
+                try {
+                    getNetdInstance()
+                            .ipSecDeleteSecurityAssociation(
+                                    mResourceId,
+                                    direction,
+                                    (mConfig.getLocalAddress() != null)
+                                            ? mConfig.getLocalAddress().getHostAddress()
+                                            : "",
+                                    (mConfig.getRemoteAddress() != null)
+                                            ? mConfig.getRemoteAddress().getHostAddress()
+                                            : "",
+                                    mConfig.getSpi(direction));
+                } catch (ServiceSpecificException e) {
+                    // FIXME: get the error code and throw is at an IOException from Errno Exception
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
+                }
+            }
+        }
+
+        @Override
+        protected void nullifyRecord() {
+            mConfig = null;
+            mResourceId = INVALID_RESOURCE_ID;
+        }
+    }
+
+    private final class SpiRecord extends ManagedResource {
+        private final int mDirection;
+        private final String mLocalAddress;
+        private final String mRemoteAddress;
+        private final IBinder mBinder;
+        private int mSpi;
+        private int mResourceId;
+
+        SpiRecord(
+                int resourceId,
+                int direction,
+                String localAddress,
+                String remoteAddress,
+                int spi,
+                IBinder binder) {
+            super(binder);
+            mResourceId = resourceId;
+            mDirection = direction;
+            mLocalAddress = localAddress;
+            mRemoteAddress = remoteAddress;
+            mSpi = spi;
+            mBinder = binder;
+        }
+
+        protected void releaseResources() {
+            try {
+                getNetdInstance()
+                        .ipSecDeleteSecurityAssociation(
+                                mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
+            } catch (ServiceSpecificException e) {
+                // FIXME: get the error code and throw is at an IOException from Errno Exception
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
+            }
+        }
+
+        protected void nullifyRecord() {
+            mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
+            mResourceId = INVALID_RESOURCE_ID;
+        }
+    }
+
+    @GuardedBy("mSpiRecords")
+    private final SparseArray<SpiRecord> mSpiRecords = new SparseArray<>();
+
+    @GuardedBy("mTransformRecords")
+    private final SparseArray<TransformRecord> mTransformRecords = new SparseArray<>();
+
+    /**
+     * Constructs a new IpSecService instance
+     *
+     * @param context Binder context for this service
+     */
+    private IpSecService(Context context) {
+        mContext = context;
+    }
+
+    static IpSecService create(Context context) throws InterruptedException {
+        final IpSecService service = new IpSecService(context);
+        service.connectNativeNetdService();
+        return service;
+    }
+
+    public void systemReady() {
+        if (isNetdAlive()) {
+            Slog.d(TAG, "IpSecService is ready");
+        } else {
+            Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
+        }
+    }
+
+    private void connectNativeNetdService() {
+        // Avoid blocking the system server to do this
+        Thread t =
+                new Thread(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                synchronized (mLock) {
+                                    NetdService.get(NETD_FETCH_TIMEOUT);
+                                }
+                            }
+                        });
+        t.run();
+    }
+
+    INetd getNetdInstance() throws RemoteException {
+        final INetd netd = NetdService.getInstance();
+        if (netd == null) {
+            throw new RemoteException("Failed to Get Netd Instance");
+        }
+        return netd;
+    }
+
+    boolean isNetdAlive() {
+        synchronized (mLock) {
+            try {
+                final INetd netd = getNetdInstance();
+                if (netd == null) {
+                    return false;
+                }
+                return netd.isAlive();
+            } catch (RemoteException re) {
+                return false;
+            }
+        }
+    }
+
+    @Override
+    /** Get a new SPI and maintain the reservation in the system server */
+    public Bundle reserveSecurityParameterIndex(
+            int direction, String remoteAddress, int requestedSpi, IBinder binder)
+            throws RemoteException {
+        int resourceId = mNextResourceId.getAndIncrement();
+
+        int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
+        String localAddress = "";
+        Bundle retBundle = new Bundle(3);
+        try {
+            spi =
+                    getNetdInstance()
+                            .ipSecAllocateSpi(
+                                    resourceId,
+                                    direction,
+                                    localAddress,
+                                    remoteAddress,
+                                    requestedSpi);
+            Log.d(TAG, "Allocated SPI " + spi);
+            retBundle.putInt(KEY_STATUS, IpSecManager.Status.OK);
+            retBundle.putInt(KEY_RESOURCE_ID, resourceId);
+            retBundle.putInt(KEY_SPI, spi);
+            synchronized (mSpiRecords) {
+                mSpiRecords.put(
+                        resourceId,
+                        new SpiRecord(
+                                resourceId, direction, localAddress, remoteAddress, spi, binder));
+            }
+        } catch (ServiceSpecificException e) {
+            // TODO: Add appropriate checks when other ServiceSpecificException types are supported
+            retBundle.putInt(KEY_STATUS, IpSecManager.Status.SPI_UNAVAILABLE);
+            retBundle.putInt(KEY_RESOURCE_ID, resourceId);
+            retBundle.putInt(KEY_SPI, spi);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return retBundle;
+    }
+
+    /** Release a previously allocated SPI that has been registered with the system server */
+    @Override
+    public void releaseSecurityParameterIndex(int resourceId) throws RemoteException {}
+
+    /**
+     * Open a socket via the system server and bind it to the specified port (random if port=0).
+     * This will return a PFD to the user that represent a bound UDP socket. The system server will
+     * cache the socket and a record of its owner so that it can and must be freed when no longer
+     * needed.
+     */
+    @Override
+    public Bundle openUdpEncapsulationSocket(int port, IBinder binder) throws RemoteException {
+        return null;
+    }
+
+    /** close a socket that has been been allocated by and registered with the system server */
+    @Override
+    public void closeUdpEncapsulationSocket(ParcelFileDescriptor socket) {}
+
+    /**
+     * Create a transport mode transform, which represent two security associations (one in each
+     * direction) in the kernel. The transform will be cached by the system server and must be freed
+     * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
+     * that are using it, which will result in all of those sockets becoming unable to send or
+     * receive data.
+     */
+    @Override
+    public Bundle createTransportModeTransform(IpSecConfig c, IBinder binder)
+            throws RemoteException {
+        // TODO: Basic input validation here since it's coming over the Binder
+        int resourceId = mNextResourceId.getAndIncrement();
+        for (int direction : DIRECTIONS) {
+            IpSecAlgorithm auth = c.getAuthentication(direction);
+            IpSecAlgorithm crypt = c.getEncryption(direction);
+            try {
+                int result =
+                        getNetdInstance()
+                                .ipSecAddSecurityAssociation(
+                                        resourceId,
+                                        c.getMode(),
+                                        direction,
+                                        (c.getLocalAddress() != null)
+                                                ? c.getLocalAddress().getHostAddress()
+                                                : "",
+                                        (c.getRemoteAddress() != null)
+                                                ? c.getRemoteAddress().getHostAddress()
+                                                : "",
+                                        (c.getNetwork() != null)
+                                                ? c.getNetwork().getNetworkHandle()
+                                                : 0,
+                                        c.getSpi(direction),
+                                        (auth != null) ? auth.getName() : "",
+                                        (auth != null) ? auth.getKey() : null,
+                                        (auth != null) ? auth.getTruncationLengthBits() : 0,
+                                        (crypt != null) ? crypt.getName() : "",
+                                        (crypt != null) ? crypt.getKey() : null,
+                                        (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+                                        c.getEncapType(),
+                                        c.getEncapLocalPort(),
+                                        c.getEncapRemotePort());
+                if (result != c.getSpi(direction)) {
+                    // TODO: cleanup the first SA if creation of second SA fails
+                    Bundle retBundle = new Bundle(2);
+                    retBundle.putInt(KEY_STATUS, IpSecManager.Status.SPI_UNAVAILABLE);
+                    retBundle.putInt(KEY_RESOURCE_ID, INVALID_RESOURCE_ID);
+                    return retBundle;
+                }
+            } catch (ServiceSpecificException e) {
+                // FIXME: get the error code and throw is at an IOException from Errno Exception
+            }
+        }
+        synchronized (mTransformRecords) {
+            mTransformRecords.put(resourceId, new TransformRecord(c, resourceId, binder));
+        }
+
+        Bundle retBundle = new Bundle(2);
+        retBundle.putInt(KEY_STATUS, IpSecManager.Status.OK);
+        retBundle.putInt(KEY_RESOURCE_ID, resourceId);
+        return retBundle;
+    }
+
+    /**
+     * Delete a transport mode transform that was previously allocated by + registered with the
+     * system server. If this is called on an inactive (or non-existent) transform, it will not
+     * return an error. It's safe to de-allocate transforms that may have already been deleted for
+     * other reasons.
+     */
+    @Override
+    public void deleteTransportModeTransform(int resourceId) throws RemoteException {
+        synchronized (mTransformRecords) {
+            TransformRecord record;
+            // We want to non-destructively get so that we can check credentials before removing
+            // this from the records.
+            record = mTransformRecords.get(resourceId);
+
+            if (record == null) {
+                throw new IllegalArgumentException(
+                        "Transform " + resourceId + " is not available to be deleted");
+            }
+
+            if (record.pid != Binder.getCallingPid() || record.uid != Binder.getCallingUid()) {
+                throw new SecurityException("Only the owner of an IpSec Transform may delete it!");
+            }
+
+            // TODO: if releaseResources() throws RemoteException, we can try again to clean up on
+            // binder death. Need to make sure that path is actually functional.
+            record.releaseResources();
+            mTransformRecords.remove(resourceId);
+            record.nullifyRecord();
+        }
+    }
+
+    /**
+     * Apply an active transport mode transform to a socket, which will apply the IPsec security
+     * association as a correspondent policy to the provided socket
+     */
+    @Override
+    public void applyTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+            throws RemoteException {
+
+        synchronized (mTransformRecords) {
+            TransformRecord info;
+            // FIXME: this code should be factored out into a security check + getter
+            info = mTransformRecords.get(resourceId);
+
+            if (info == null) {
+                throw new IllegalArgumentException("Transform " + resourceId + " is not active");
+            }
+
+            // TODO: make this a function.
+            if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
+                throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
+            }
+
+            IpSecConfig c = info.getConfig();
+            try {
+                for (int direction : DIRECTIONS) {
+                    getNetdInstance()
+                            .ipSecApplyTransportModeTransform(
+                                    socket.getFileDescriptor(),
+                                    resourceId,
+                                    direction,
+                                    (c.getLocalAddress() != null)
+                                            ? c.getLocalAddress().getHostAddress()
+                                            : "",
+                                    (c.getRemoteAddress() != null)
+                                            ? c.getRemoteAddress().getHostAddress()
+                                            : "",
+                                    c.getSpi(direction));
+                }
+            } catch (ServiceSpecificException e) {
+                // FIXME: get the error code and throw is at an IOException from Errno Exception
+            }
+        }
+    }
+    /**
+     * Remove a transport mode transform from a socket, applying the default (empty) policy. This
+     * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
+     * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
+     * used: reserved for future improved input validation.
+     */
+    @Override
+    public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+            throws RemoteException {
+        try {
+            getNetdInstance().ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
+        } catch (ServiceSpecificException e) {
+            // FIXME: get the error code and throw is at an IOException from Errno Exception
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+        pw.println("IpSecService Log:");
+        pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index e127eb9..8404025 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -42,6 +42,7 @@
  *
  * @hide
  */
+@VisibleForTesting
 public class NetworkScorerAppManager {
     private static final String TAG = "NetworkScorerAppManager";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -63,7 +64,8 @@
      * Returns the list of available scorer apps. The list will be empty if there are
      * no valid scorers.
      */
-    List<NetworkScorerAppData> getAllValidScorers() {
+    @VisibleForTesting
+    public List<NetworkScorerAppData> getAllValidScorers() {
         if (VERBOSE) Log.v(TAG, "getAllValidScorers()");
         final PackageManager pm = mContext.getPackageManager();
         final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
@@ -168,7 +170,8 @@
      *     it was disabled or uninstalled).
      */
     @Nullable
-    NetworkScorerAppData getActiveScorer() {
+    @VisibleForTesting
+    public NetworkScorerAppData getActiveScorer() {
         final int enabledSetting = getNetworkRecommendationsEnabledSetting();
         if (enabledSetting == NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF) {
             return null;
@@ -211,7 +214,8 @@
      * @return true if the scorer was changed, or false if the package is not a valid scorer or
      *         a valid network recommendation provider exists.
      */
-    boolean setActiveScorer(String packageName) {
+    @VisibleForTesting
+    public boolean setActiveScorer(String packageName) {
         final String oldPackageName = getNetworkRecommendationsPackage();
 
         if (TextUtils.equals(oldPackageName, packageName)) {
@@ -246,7 +250,8 @@
      * is no longer valid then {@link Settings.Global#NETWORK_RECOMMENDATIONS_ENABLED} will be set
      * to <code>0</code> (disabled).
      */
-    void updateState() {
+    @VisibleForTesting
+    public void updateState() {
         final int enabledSetting = getNetworkRecommendationsEnabledSetting();
         if (enabledSetting == NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF) {
             // Don't change anything if it's forced off.
@@ -284,7 +289,8 @@
     /**
      * Migrates the NETWORK_SCORER_APP Setting to the USE_OPEN_WIFI_PACKAGE Setting.
      */
-    void migrateNetworkScorerAppSettingIfNeeded() {
+    @VisibleForTesting
+    public void migrateNetworkScorerAppSettingIfNeeded() {
         final String scorerAppPkgNameSetting =
                 mSettingsFacade.getString(mContext, Settings.Global.NETWORK_SCORER_APP);
         if (TextUtils.isEmpty(scorerAppPkgNameSetting)) {
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 0610464..7f5befa 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -20,25 +20,46 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.hardware.GeomagneticField;
 import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
-public class SensorNotificationService extends SystemService implements SensorEventListener {
-    //TODO: set DBG to false or remove Slog before release
-    private static final boolean DBG = true;
+public class SensorNotificationService extends SystemService
+        implements SensorEventListener, LocationListener {
+    private static final boolean DBG = false;
     private static final String TAG = "SensorNotificationService";
-    private Context mContext;
 
+    private static final long MINUTE_IN_MS = 60 * 1000;
+    private static final long KM_IN_M = 1000;
+
+    private static final long LOCATION_MIN_TIME = 30 * MINUTE_IN_MS;
+    private static final long LOCATION_MIN_DISTANCE = 100 * KM_IN_M;
+
+    private static final String PROPERTY_USE_MOCKED_LOCATION =
+            "sensor.notification.use_mocked"; // max key length is 32
+
+    private static final long MILLIS_2010_1_1 = 1262358000000l;
+
+    private Context mContext;
     private SensorManager mSensorManager;
+    private LocationManager mLocationManager;
     private Sensor mMetaSensor;
 
+    // for rate limiting
+    private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
+
     public SensorNotificationService(Context context) {
         super(context);
         mContext = context;
@@ -50,7 +71,6 @@
 
     public void onBootPhase(int phase) {
         if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-            // start
             mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
             mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META);
             if (mMetaSensor == null) {
@@ -60,13 +80,28 @@
                         SensorManager.SENSOR_DELAY_FASTEST);
             }
         }
+
+        if (phase == PHASE_BOOT_COMPLETED) {
+            // LocationManagerService is initialized after PHASE_THIRD_PARTY_APPS_CAN_START
+            mLocationManager =
+                    (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+            if (mLocationManager == null) {
+                if (DBG) Slog.d(TAG, "Cannot obtain location service.");
+            } else {
+                mLocationManager.requestLocationUpdates(
+                        LocationManager.PASSIVE_PROVIDER,
+                        LOCATION_MIN_TIME,
+                        LOCATION_MIN_DISTANCE,
+                        this);
+            }
+        }
     }
 
     private void broadcastDynamicSensorChanged() {
         Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
         i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers
         mContext.sendBroadcastAsUser(i, UserHandle.ALL);
-        if (DBG) Slog.d(TAG, "DYNS sent dynamic sensor broadcast");
+        if (DBG) Slog.d(TAG, "dynamic sensor broadcast sent");
     }
 
     @Override
@@ -77,8 +112,62 @@
     }
 
     @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+    public void onLocationChanged(Location location) {
+        if (DBG) Slog.d(TAG, String.format(
+                "Location is (%f, %f), h %f, acc %f, mocked %b",
+                location.getLatitude(), location.getLongitude(),
+                location.getAltitude(), location.getAccuracy(),
+                location.isFromMockProvider()));
 
+        // lat long == 0 usually means invalid location
+        if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+            return;
+        }
+
+        // update too often, ignore
+        if (SystemClock.elapsedRealtime() - mLocalGeomagneticFieldUpdateTime < 10 * MINUTE_IN_MS) {
+            return;
+        }
+
+        long time = System.currentTimeMillis();
+        // Mocked location should not be used. Except in test, only use mocked location
+        // Wrong system clock also gives bad values so ignore as well.
+        if (useMockedLocation() == location.isFromMockProvider() || time < MILLIS_2010_1_1) {
+            return;
+        }
+
+        GeomagneticField field = new GeomagneticField(
+                (float) location.getLatitude(), (float) location.getLongitude(),
+                (float) location.getAltitude(), time);
+        if (DBG) Slog.d(TAG, String.format(
+                "Nominal mag field, norm %fuT, decline %f deg, incline %f deg",
+                field.getFieldStrength() / 1000, field.getDeclination(), field.getInclination()));
+
+        try {
+            SensorAdditionalInfo info = SensorAdditionalInfo.createLocalGeomagneticField(
+                        field.getFieldStrength() / 1000, // convert from nT to uT
+                        (float)(field.getDeclination() * Math.PI / 180), // from degree to rad
+                        (float)(field.getInclination() * Math.PI / 180)); // from degree to rad
+            if (info != null) {
+                mSensorManager.setOperationParameter(info);
+                mLocalGeomagneticFieldUpdateTime = SystemClock.elapsedRealtime();
+            }
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Invalid local geomagnetic field, ignore.");
+        }
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+    @Override
+    public void onStatusChanged(String provider, int status, Bundle extras) {}
+    @Override
+    public void onProviderEnabled(String provider) {}
+    @Override
+    public void onProviderDisabled(String provider) {}
+
+    private boolean useMockedLocation() {
+        return "false".equals(System.getProperty(PROPERTY_USE_MOCKED_LOCATION, "false"));
     }
 }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7c1a609..dd4d906 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -32,37 +32,39 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.telephony.CellLocation;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
 import android.telephony.CellInfo;
-import android.telephony.VoLteServiceState;
+import android.telephony.CellLocation;
 import android.telephony.DisconnectCause;
+import android.telephony.PhoneStateListener;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.PreciseDisconnectCause;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.VoLteServiceState;
 import android.text.TextUtils;
-import android.text.format.Time;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.util.LocalLog;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
-import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.PhoneConstantConversions;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.am.BatteryStatsService;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Since phone process can be restarted, this class provides a centralized place
  * that applications can register and be called back from.
@@ -159,8 +161,6 @@
 
     private String[] mDataConnectionReason;
 
-    private String[] mDataConnectionApn;
-
     private ArrayList<String>[] mConnectedApns;
 
     private LinkProperties[] mDataConnectionLinkProperties;
@@ -191,6 +191,8 @@
 
     private boolean mCarrierNetworkChangeState = false;
 
+    private final LocalLog mLocalLog = new LocalLog(100);
+
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
@@ -310,7 +312,6 @@
         mMessageWaiting = new boolean[numPhones];
         mDataConnectionPossible = new boolean[numPhones];
         mDataConnectionReason = new String[numPhones];
-        mDataConnectionApn = new String[numPhones];
         mCallForwarding = new boolean[numPhones];
         mCellLocation = new Bundle[numPhones];
         mDataConnectionLinkProperties = new LinkProperties[numPhones];
@@ -329,7 +330,6 @@
             mCallForwarding[i] =  false;
             mDataConnectionPossible[i] = false;
             mDataConnectionReason[i] =  "";
-            mDataConnectionApn[i] =  "";
             mCellLocation[i] = new Bundle();
             mCellInfo.add(i, null);
             mConnectedApns[i] = new ArrayList<String>();
@@ -536,7 +536,6 @@
                 if (DBG) {
                     log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
                 }
-                if (VDBG) toStringLogSSC("listen");
                 if (notifyNow && validatePhoneId(phoneId)) {
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                         try {
@@ -780,14 +779,14 @@
         }
 
         synchronized (mRecords) {
+            String str = "notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId
+                    + " state=" + state;
             if (VDBG) {
-                log("notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId
-                    + " state=" + state);
+                log(str);
             }
+            mLocalLog.log(str);
             if (validatePhoneId(phoneId)) {
                 mServiceState[phoneId] = state;
-                logServiceStateChanged("notifyServiceStateForSubscriber", subId, phoneId, state);
-                if (VDBG) toStringLogSSC("notifyServiceStateForSubscriber");
 
                 for (Record r : mRecords) {
                     if (VDBG) {
@@ -885,7 +884,6 @@
         if (VDBG) {
             log("notifySignalStrengthForPhoneId: subId=" + subId
                 +" phoneId=" + phoneId + " signalStrength=" + signalStrength);
-            toStringLogSSC("notifySignalStrengthForPhoneId");
         }
 
         synchronized (mRecords) {
@@ -1137,18 +1135,20 @@
                     modified = true;
                 }
                 if (modified) {
-                    if (DBG) {
-                        log("onDataConnectionStateChanged(" + mDataConnectionState[phoneId]
-                            + ", " + mDataConnectionNetworkType[phoneId] + ")");
-                    }
+                    String str = "onDataConnectionStateChanged(" + mDataConnectionState[phoneId]
+                            + ", " + mDataConnectionNetworkType[phoneId] + ")";
+                    log(str);
+                    mLocalLog.log(str);
                     for (Record r : mRecords) {
                         if (r.matchPhoneStateListenerEvent(
                                 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) &&
                                 idMatch(r.subId, subId, phoneId)) {
                             try {
-                                log("Notify data connection state changed on sub: " +
-                                        subId);
-                                r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
+                                if (DBG) {
+                                    log("Notify data connection state changed on sub: " + subId);
+                                }
+                                r.callback.onDataConnectionStateChanged(
+                                        mDataConnectionState[phoneId],
                                         mDataConnectionNetworkType[phoneId]);
                             } catch (RemoteException ex) {
                                 mRemoveList.add(r.binder);
@@ -1163,7 +1163,8 @@
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
                         try {
-                            r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState);
+                            r.callback.onPreciseDataConnectionStateChanged(
+                                    mPreciseDataConnectionState);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -1391,36 +1392,58 @@
     }
 
     @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
         synchronized (mRecords) {
             final int recordCount = mRecords.size();
             pw.println("last known state:");
+            pw.increaseIndent();
             for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
-                pw.println("  Phone Id=" + i);
-                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]);
-                pw.println("  mDataActivity=" + mDataActivity[i]);
-                pw.println("  mDataConnectionState=" + mDataConnectionState[i]);
-                pw.println("  mDataConnectionPossible=" + mDataConnectionPossible[i]);
-                pw.println("  mDataConnectionReason=" + mDataConnectionReason[i]);
-                pw.println("  mDataConnectionApn=" + mDataConnectionApn[i]);
-                pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]);
-                pw.println("  mDataConnectionNetworkCapabilities=" +
+                pw.println("Phone Id=" + i);
+                pw.increaseIndent();
+                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]);
+                pw.println("mDataActivity=" + mDataActivity[i]);
+                pw.println("mDataConnectionState=" + mDataConnectionState[i]);
+                pw.println("mDataConnectionPossible=" + mDataConnectionPossible[i]);
+                pw.println("mDataConnectionReason=" + mDataConnectionReason[i]);
+                pw.println("mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]);
+                pw.println("mDataConnectionNetworkCapabilities=" +
                         mDataConnectionNetworkCapabilities[i]);
-                pw.println("  mCellLocation=" + mCellLocation[i]);
-                pw.println("  mCellInfo=" + mCellInfo.get(i));
+                pw.println("mCellLocation=" + mCellLocation[i]);
+                pw.println("mCellInfo=" + mCellInfo.get(i));
+                pw.decreaseIndent();
             }
+            pw.println("mConnectedApns=" + Arrays.toString(mConnectedApns));
+            pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
+            pw.println("mPreciseCallState=" + mPreciseCallState);
+            pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
+            pw.println("mRingingCallState=" + mRingingCallState);
+            pw.println("mForegroundCallState=" + mForegroundCallState);
+            pw.println("mBackgroundCallState=" + mBackgroundCallState);
+            pw.println("mVoLteServiceState=" + mVoLteServiceState);
+
+            pw.decreaseIndent();
+
+            pw.println("local logs:");
+            pw.increaseIndent();
+            mLocalLog.dump(fd, pw, args);
+            pw.decreaseIndent();
             pw.println("registrations: count=" + recordCount);
+            pw.increaseIndent();
             for (Record r : mRecords) {
-                pw.println("  " + r);
+                pw.println(r);
             }
+            pw.decreaseIndent();
         }
     }
 
@@ -1705,63 +1728,6 @@
         Rlog.d(TAG, s);
     }
 
-    private static class LogSSC {
-        private Time mTime;
-        private String mS;
-        private int mSubId;
-        private int mPhoneId;
-        private ServiceState mState;
-
-        public void set(Time t, String s, int subId, int phoneId, ServiceState state) {
-            mTime = t; mS = s; mSubId = subId; mPhoneId = phoneId; mState = state;
-        }
-
-        @Override
-        public String toString() {
-            return mS + " Time " + mTime.toString() + " mSubId " + mSubId + " mPhoneId "
-                    + mPhoneId + "  mState " + mState;
-        }
-    }
-
-    private LogSSC logSSC [] = new LogSSC[10];
-    private int next = 0;
-
-    private void logServiceStateChanged(String s, int subId, int phoneId, ServiceState state) {
-        if (logSSC == null || logSSC.length == 0) {
-            return;
-        }
-        if (logSSC[next] == null) {
-            logSSC[next] = new LogSSC();
-        }
-        Time t = new Time();
-        t.setToNow();
-        logSSC[next].set(t, s, subId, phoneId, state);
-        if (++next >= logSSC.length) {
-            next = 0;
-        }
-    }
-
-    private void toStringLogSSC(String prompt) {
-        if (logSSC == null || logSSC.length == 0 || (next == 0 && logSSC[next] == null)) {
-            log(prompt + ": logSSC is empty");
-        } else {
-            // There is at least one element
-            log(prompt + ": logSSC.length=" + logSSC.length + " next=" + next);
-            int i = next;
-            if (logSSC[i] == null) {
-                // logSSC is not full so back to the beginning
-                i = 0;
-            }
-            do {
-                log(logSSC[i].toString());
-                if (++i >= logSSC.length) {
-                    i = 0;
-                }
-            } while (i != next);
-            log(prompt + ": ----------------");
-        }
-    }
-
     boolean idMatch(int rSubId, int subId, int phoneId) {
 
         if(subId < 0) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e560d32..738365d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -427,14 +427,13 @@
     public boolean addAccountExplicitlyWithVisibility(Account account, String password,
             Bundle extras, Map packageToVisibility) {
         Bundle.setDefusable(extras, true);
-
-        final int callingUid = Binder.getCallingUid();
+        int callingUid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         Preconditions.checkNotNull(account, "account cannot be null");
-        int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format("uid %s cannot explicitly add accounts of type: %s",
                     callingUid, account.type);
@@ -461,9 +460,9 @@
     public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
             String accountType) {
         int callingUid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
         boolean isSystemUid = UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
-        List<String> managedTypes =
-                getTypesForCaller(callingUid, UserHandle.getUserId(callingUid), isSystemUid);
+        List<String> managedTypes = getTypesForCaller(callingUid, userId, isSystemUid);
 
         if ((accountType != null && !managedTypes.contains(accountType))
                 || (accountType == null && !isSystemUid)) {
@@ -478,8 +477,9 @@
 
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid,
-                    getUserAccounts(UserHandle.getUserId(callingUid)));
+                    accounts);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -490,12 +490,8 @@
      */
     private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
             List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
-        int uid = 0;
-        try {
-            uid = mPackageManager.getPackageUidAsUser(packageName,
-                    UserHandle.getUserId(callingUid));
-        } catch (NameNotFoundException e) {
-            Log.d(TAG, "Package not found " + e.getMessage());
+        if (!packageExistsForUser(packageName, accounts.userId)) {
+            Log.d(TAG, "Package not found " + packageName);
             return new LinkedHashMap<>();
         }
 
@@ -520,19 +516,26 @@
     public Map<String, Integer> getPackagesAndVisibilityForAccount(Account account) {
         Preconditions.checkNotNull(account, "account cannot be null");
         int callingUid = Binder.getCallingUid();
-        int userId = UserHandle.getUserId(callingUid);
-        UserAccounts accounts = getUserAccounts(userId);
+        int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)
                 && !isSystemUid(callingUid)) {
             String msg =
                     String.format("uid %s cannot get secrets for account %s", callingUid, account);
             throw new SecurityException(msg);
         }
-        synchronized (accounts.dbLock) {
-            synchronized (accounts.cacheLock) {
-                return getPackagesAndVisibilityForAccountLocked(account, accounts);
+
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            synchronized (accounts.dbLock) {
+                synchronized (accounts.cacheLock) {
+                    return getPackagesAndVisibilityForAccountLocked(account, accounts);
+                }
             }
+        } finally {
+            restoreCallingIdentity(identityToken);
         }
+
     }
 
     /**
@@ -560,8 +563,8 @@
         Preconditions.checkNotNull(account, "account cannot be null");
         Preconditions.checkNotNull(packageName, "packageName cannot be null");
         int callingUid = Binder.getCallingUid();
-        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
-        if (!isAccountManagedByCaller(account.type, callingUid, accounts.userId)
+        int userId = UserHandle.getCallingUserId();
+        if (!isAccountManagedByCaller(account.type, callingUid, userId)
             && !isSystemUid(callingUid)) {
             String msg = String.format(
                     "uid %s cannot get secrets for accounts of type: %s",
@@ -569,7 +572,13 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        return resolveAccountVisibility(account, packageName, accounts);
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            return resolveAccountVisibility(account, packageName, accounts);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
     }
 
     /**
@@ -708,8 +717,8 @@
         Preconditions.checkNotNull(account, "account cannot be null");
         Preconditions.checkNotNull(packageName, "packageName cannot be null");
         int callingUid = Binder.getCallingUid();
-        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
-        if (!isAccountManagedByCaller(account.type, callingUid, accounts.userId)
+        int userId = UserHandle.getCallingUserId();
+        if (!isAccountManagedByCaller(account.type, callingUid, userId)
             && !isSystemUid(callingUid)) {
             String msg = String.format(
                     "uid %s cannot get secrets for accounts of type: %s",
@@ -717,8 +726,14 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
                 accounts);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
     }
 
     /**
@@ -805,8 +820,15 @@
     public void registerAccountListener(String[] accountTypes, String opPackageName) {
         int callingUid = Binder.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, opPackageName);
-        registerAccountListener(accountTypes, opPackageName,
-            getUserAccounts(UserHandle.getUserId(callingUid)));
+
+        int userId = UserHandle.getCallingUserId();
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            registerAccountListener(accountTypes, opPackageName, accounts);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
     }
 
     private void registerAccountListener(String[] accountTypes, String opPackageName,
@@ -832,7 +854,18 @@
     public void unregisterAccountListener(String[] accountTypes, String opPackageName) {
         int callingUid = Binder.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, opPackageName);
-        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
+        int userId = UserHandle.getCallingUserId();
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            unregisterAccountListener(accountTypes, opPackageName, accounts);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void unregisterAccountListener(String[] accountTypes, String opPackageName,
+            UserAccounts accounts) {
         synchronized (accounts.mReceiversForType) {
             if (accountTypes == null) {
                 // null for any type
@@ -903,7 +936,7 @@
             long identityToken = clearCallingIdentity();
             try {
                 mPackageManager.getPackageUidAsUser(packageName, userId);
-                return true; // package exist
+                return true;
             } finally {
                 restoreCallingIdentity(identityToken);
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4cbfb27..7dd75df 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -31,8 +31,10 @@
 
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
+import android.app.ServiceStartArgs;
 import android.content.IIntentSender;
 import android.content.IntentSender;
+import android.content.pm.ParceledListSlice;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadObjectException;
@@ -369,7 +371,8 @@
                     }
                     // This app knows it is in the new model where this operation is not
                     // allowed, so tell it what has happened.
-                    return new ComponentName("?", "app is in background");
+                    UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
+                    return new ComponentName("?", "app is in background uid " + uidRec);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -1956,78 +1959,86 @@
             return;
         }
 
-        while (r.pendingStarts.size() > 0) {
-            Exception caughtException = null;
-            ServiceRecord.StartItem si = null;
-            try {
-                si = r.pendingStarts.remove(0);
-                if (DEBUG_SERVICE) {
-                    Slog.v(TAG_SERVICE, "Sending arguments to: "
-                            + r + " " + r.intent + " args=" + si.intent);
-                }
-                if (si.intent == null && N > 1) {
-                    // If somehow we got a dummy null intent in the middle,
-                    // then skip it.  DO NOT skip a null intent when it is
-                    // the only one in the list -- this is to support the
-                    // onStartCommand(null) case.
-                    continue;
-                }
-                si.deliveredTime = SystemClock.uptimeMillis();
-                r.deliveredStarts.add(si);
-                si.deliveryCount++;
-                if (si.neededGrants != null) {
-                    mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
-                            si.getUriPermissionsLocked());
-                }
-                mAm.grantEphemeralAccessLocked(r.userId, si.intent,
-                        r.appInfo.uid, UserHandle.getAppId(si.callingId));
-                bumpServiceExecutingLocked(r, execInFg, "start");
-                if (!oomAdjusted) {
-                    oomAdjusted = true;
-                    mAm.updateOomAdjLocked(r.app);
-                }
-                if (r.fgRequired && !r.fgWaiting) {
-                    if (!r.isForeground) {
-                        if (DEBUG_BACKGROUND_CHECK) {
-                            Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
-                        }
-                        scheduleServiceForegroundTransitionTimeoutLocked(r);
-                    } else {
-                        if (DEBUG_BACKGROUND_CHECK) {
-                            Slog.i(TAG, "Service already foreground; no new timeout: " + r);
-                        }
-                        r.fgRequired = false;
-                    }
-                }
-                int flags = 0;
-                if (si.deliveryCount > 1) {
-                    flags |= Service.START_FLAG_RETRY;
-                }
-                if (si.doneExecutingCount > 0) {
-                    flags |= Service.START_FLAG_REDELIVERY;
-                }
-                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
-            } catch (TransactionTooLargeException e) {
-                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
-                        + si.intent);
-                caughtException = e;
-            } catch (RemoteException e) {
-                // Remote process gone...  we'll let the normal cleanup take care of this.
-                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
-                caughtException = e;
-            } catch (Exception e) {
-                Slog.w(TAG, "Unexpected exception", e);
-                caughtException = e;
-            }
+        ArrayList<ServiceStartArgs> args = new ArrayList<>();
 
-            if (caughtException != null) {
-                // Keep nesting count correct
-                final boolean inDestroying = mDestroyingServices.contains(r);
-                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
-                if (caughtException instanceof TransactionTooLargeException) {
-                    throw (TransactionTooLargeException)caughtException;
+        while (r.pendingStarts.size() > 0) {
+            ServiceRecord.StartItem si = r.pendingStarts.remove(0);
+            if (DEBUG_SERVICE) {
+                Slog.v(TAG_SERVICE, "Sending arguments to: "
+                        + r + " " + r.intent + " args=" + si.intent);
+            }
+            if (si.intent == null && N > 1) {
+                // If somehow we got a dummy null intent in the middle,
+                // then skip it.  DO NOT skip a null intent when it is
+                // the only one in the list -- this is to support the
+                // onStartCommand(null) case.
+                continue;
+            }
+            si.deliveredTime = SystemClock.uptimeMillis();
+            r.deliveredStarts.add(si);
+            si.deliveryCount++;
+            if (si.neededGrants != null) {
+                mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
+                        si.getUriPermissionsLocked());
+            }
+            mAm.grantEphemeralAccessLocked(r.userId, si.intent,
+                    r.appInfo.uid, UserHandle.getAppId(si.callingId));
+            bumpServiceExecutingLocked(r, execInFg, "start");
+            if (!oomAdjusted) {
+                oomAdjusted = true;
+                mAm.updateOomAdjLocked(r.app);
+            }
+            if (r.fgRequired && !r.fgWaiting) {
+                if (!r.isForeground) {
+                    if (DEBUG_BACKGROUND_CHECK) {
+                        Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
+                    }
+                    scheduleServiceForegroundTransitionTimeoutLocked(r);
+                } else {
+                    if (DEBUG_BACKGROUND_CHECK) {
+                        Slog.i(TAG, "Service already foreground; no new timeout: " + r);
+                    }
+                    r.fgRequired = false;
                 }
-                break;
+            }
+            int flags = 0;
+            if (si.deliveryCount > 1) {
+                flags |= Service.START_FLAG_RETRY;
+            }
+            if (si.doneExecutingCount > 0) {
+                flags |= Service.START_FLAG_REDELIVERY;
+            }
+            args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
+        }
+
+        ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
+        slice.setInlineCountLimit(4);
+        Exception caughtException = null;
+        try {
+            r.app.thread.scheduleServiceArgs(r, slice);
+        } catch (TransactionTooLargeException e) {
+            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
+                    + " args, first: " + args.get(0).args);
+            Slog.w(TAG, "Failed delivering service starts", e);
+            caughtException = e;
+        } catch (RemoteException e) {
+            // Remote process gone...  we'll let the normal cleanup take care of this.
+            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
+            Slog.w(TAG, "Failed delivering service starts", e);
+            caughtException = e;
+        } catch (Exception e) {
+            Slog.w(TAG, "Unexpected exception", e);
+            caughtException = e;
+        }
+
+        if (caughtException != null) {
+            // Keep nesting count correct
+            final boolean inDestroying = mDestroyingServices.contains(r);
+            for (int i = 0; i < args.size(); i++) {
+                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
+            }
+            if (caughtException instanceof TransactionTooLargeException) {
+                throw (TransactionTooLargeException)caughtException;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19fc2b8..ee2fdba 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12143,6 +12143,24 @@
         return false;
     }
 
+    @Override
+    public void backgroundWhitelistUid(final int uid) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the OS may call backgroundWhitelistUid()");
+        }
+
+        if (DEBUG_BACKGROUND_CHECK) {
+            Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
+        }
+        synchronized (this) {
+            final int N = mBackgroundUidWhitelist.length;
+            int[] newList = new int[N+1];
+            System.arraycopy(mBackgroundUidWhitelist, 0, newList, 0, N);
+            newList[N] = uid;
+            mBackgroundUidWhitelist = newList;
+        }
+    }
+
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             String abiOverride) {
         ProcessRecord app;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b72cd73..3e3fee5 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -745,7 +745,7 @@
     }
 
     /**
-     * Returns a {@link TaskRecord} for the input id if available. Null otherwise.
+     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
      * @param id Id of the task we would like returned.
      * @param matchMode The mode to match the given task id in.
      * @param stackId The stack to restore the task to (default launch stack will be used if
@@ -765,7 +765,7 @@
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 ActivityStack stack = stacks.get(stackNdx);
-                TaskRecord task = stack.taskForIdLocked(id);
+                final TaskRecord task = stack.taskForIdLocked(id);
                 if (task != null) {
                     return task;
                 }
@@ -780,11 +780,17 @@
         // Otherwise, check the recent tasks and return if we find it there and we are not restoring
         // the task from recents
         if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
-        TaskRecord task = mRecentTasks.taskForIdLocked(id);
-        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
-            if (DEBUG_RECENTS && task == null) {
+        final TaskRecord task = mRecentTasks.taskForIdLocked(id);
+
+        if (task == null) {
+            if (DEBUG_RECENTS) {
                 Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
             }
+
+            return null;
+        }
+
+        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
             return task;
         }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e5b2eca..983c975 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1021,32 +1021,24 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                Slog.d(TAG, "begin setBatteryStateLocked");
-                try {
-                    synchronized (mStats) {
-                        final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
-                        if (mStats.isOnBattery() == onBattery) {
-                            // The battery state has not changed, so we don't need to sync external
-                            // stats immediately.
-                            mStats.setBatteryStateLocked(status, health, plugType, level, temp,
-                                    volt,
-                                    chargeUAh, chargeFullUAh);
-                            return;
-                        }
+                synchronized (mStats) {
+                    final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
+                    if (mStats.isOnBattery() == onBattery) {
+                        // The battery state has not changed, so we don't need to sync external
+                        // stats immediately.
+                        mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
+                                chargeUAh, chargeFullUAh);
+                        return;
                     }
-                } finally {
-                    Slog.d(TAG, "end setBatteryStateLocked");
                 }
 
                 // Sync external stats first as the battery has changed states. If we don't sync
                 // immediately here, we may not collect the relevant data later.
                 updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
-                Slog.d(TAG, "begin setBatteryStateLocked");
                 synchronized (mStats) {
                     mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
                             chargeUAh, chargeFullUAh);
                 }
-                Slog.d(TAG, "end setBatteryStateLocked");
             }
         });
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 728476a..3b5e5bc 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -670,7 +670,7 @@
             }
             mInjector.systemServiceManagerCleanupUser(userId);
             synchronized (mLock) {
-                mInjector.stackSupervisorRemoveUserLocked(userId);
+                mInjector.getActivityStackSupervisor().removeUserLocked(userId);
             }
             // Remove the user if it is ephemeral.
             if (getUserInfo(userId).isEphemeral()) {
@@ -823,8 +823,10 @@
                     return true;
                 }
 
-                mInjector.stackSupervisorSetLockTaskModeLocked(null,
-                        ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+                if (foreground) {
+                    mInjector.getActivityStackSupervisor().setLockTaskModeLocked(
+                            null, ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+                }
 
                 final UserInfo userInfo = getUserInfo(userId);
                 if (userInfo == null) {
@@ -1202,11 +1204,12 @@
     }
 
     void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
-        boolean homeInFront = mInjector.stackSupervisorSwitchUserLocked(newUserId, uss);
+        boolean homeInFront =
+                mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss);
         if (homeInFront) {
             mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground");
         } else {
-            mInjector.stackSupervisorResumeFocusedStackTopActivityLocked();
+            mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked();
         }
         EventLogTags.writeAmSwitchUser(newUserId);
         sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
@@ -1680,10 +1683,6 @@
             mService.mSystemServiceManager.cleanupUser(userId);
         }
 
-        void stackSupervisorRemoveUserLocked(int userId) {
-            mService.mStackSupervisor.removeUserLocked(userId);
-        }
-
         protected UserManagerService getUserManager() {
             if (mUserManager == null) {
                 IBinder b = ServiceManager.getService(Context.USER_SERVICE);
@@ -1743,24 +1742,10 @@
             return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
         }
 
-        boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) {
-            return mService.mStackSupervisor.switchUserLocked(userId, uss);
-        }
-
         void startHomeActivityLocked(int userId, String reason) {
             mService.startHomeActivityLocked(userId, reason);
         }
 
-        void stackSupervisorResumeFocusedStackTopActivityLocked() {
-            mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
-        }
-
-        void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState,
-                String reason, boolean andResume) {
-            mService.mStackSupervisor.setLockTaskModeLocked(task, lockTaskModeState, reason,
-                    andResume);
-        }
-
         void updateUserConfigurationLocked() {
             mService.updateUserConfigurationLocked();
         }
@@ -1778,5 +1763,9 @@
                     true /* above system */);
             d.show();
         }
+
+        ActivityStackSupervisor getActivityStackSupervisor() {
+            return mService.mStackSupervisor;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 59f8a2d..6f593b0 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -86,22 +86,18 @@
         final List<InstantAppResolveInfo> instantAppResolveInfoList =
                 connection.getInstantAppResolveInfoList(shaPrefix, token);
 
-        final AuxiliaryResolveInfo resolveInfo;
         if (instantAppResolveInfoList == null || instantAppResolveInfoList.size() == 0) {
             // No hash prefix match; there are no instant apps for this domain.
             if (DEBUG_EPHEMERAL) {
                 Log.d(TAG, "No results returned");
             }
-            resolveInfo = null;
-        } else {
-            resolveInfo = InstantAppResolver.filterInstantAppIntent(instantAppResolveInfoList,
-                    intent, requestObj.resolvedType, requestObj.userId,
-                    intent.getPackage(), digest, token);
+            return null;
         }
-
+        final AuxiliaryResolveInfo resolveInfo = InstantAppResolver.filterInstantAppIntent(
+                instantAppResolveInfoList, intent, requestObj.resolvedType, requestObj.userId,
+                intent.getPackage(), digest, token);
         logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
-                resolveInfo != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE);
-
+                RESOLUTION_SUCCESS);
         return resolveInfo;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index d9ea728..acbd446 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -151,6 +151,7 @@
         // TODO(calin,jeffhao): shared library paths should be adjusted to include previous code
         // paths (b/34169257).
         final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
+        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
         final int dexoptFlags = getDexFlags(pkg, compilerFilter);
 
         int result = DEX_OPT_SKIPPED;
@@ -254,6 +255,8 @@
     @GuardedBy("mInstallLock")
     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
             String compilerFilter, boolean isUsedByOtherApps) {
+        compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
+        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
         int dexoptFlags = getDexFlags(info, compilerFilter) | DEXOPT_SECONDARY_DEX;
         // Check the app storage and add the appropriate flags.
         if (info.dataDir.equals(info.deviceProtectedDataDir)) {
@@ -264,7 +267,6 @@
             Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
             return DEX_OPT_FAILED;
         }
-        compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
         Log.d(TAG, "Running dexopt on: " + path
                 + " pkg=" + info.packageName + " isa=" + isas
                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3303081..f62f115 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -608,6 +608,10 @@
     final boolean mIsPreNUpgrade;
     final boolean mIsPreNMR1Upgrade;
 
+    // Have we told the Activity Manager to whitelist the default container service by uid yet?
+    @GuardedBy("mPackages")
+    boolean mDefaultContainerWhitelisted = false;
+
     @GuardedBy("mPackages")
     private boolean mDexOptDialogShown;
 
@@ -3057,9 +3061,17 @@
                 | MATCH_DIRECT_BOOT_UNAWARE
                 | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
         final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
-        final List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
+        List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
                 resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
-
+        // temporarily look for the old action
+        if (resolvers.size() == 0) {
+            if (DEBUG_EPHEMERAL) {
+                Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
+            }
+            resolverIntent.setAction(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
+            resolvers = queryIntentServicesInternal(resolverIntent, null,
+                    resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
+        }
         final int N = resolvers.size();
         if (N == 0) {
             if (DEBUG_EPHEMERAL) {
@@ -3106,8 +3118,17 @@
                 MATCH_DIRECT_BOOT_AWARE
                 | MATCH_DIRECT_BOOT_UNAWARE
                 | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+        List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
                 resolveFlags, UserHandle.USER_SYSTEM);
+        // temporarily look for the old action
+        if (matches.isEmpty()) {
+            if (DEBUG_EPHEMERAL) {
+                Slog.d(TAG, "Ephemeral installer not found with new action; try old one");
+            }
+            intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
+            matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+                    resolveFlags, UserHandle.USER_SYSTEM);
+        }
         Iterator<ResolveInfo> iter = matches.iterator();
         while (iter.hasNext()) {
             final ResolveInfo rInfo = iter.next();
@@ -3136,8 +3157,17 @@
                 .addCategory(Intent.CATEGORY_DEFAULT)
                 .setPackage(resolver.getPackageName());
         final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
+        List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
                 UserHandle.USER_SYSTEM);
+        // temporarily look for the old action
+        if (matches.isEmpty()) {
+            if (DEBUG_EPHEMERAL) {
+                Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one");
+            }
+            intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS);
+            matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
+                    UserHandle.USER_SYSTEM);
+        }
         if (matches.isEmpty()) {
             return null;
         }
@@ -5723,20 +5753,23 @@
         synchronized (mPackages) {
             final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
             for (int n = 0; n < count; n++) {
-                ResolveInfo info = resolvedActivities.get(n);
-                String packageName = info.activityInfo.packageName;
-                PackageSetting ps = mSettings.mPackages.get(packageName);
+                final ResolveInfo info = resolvedActivities.get(n);
+                final String packageName = info.activityInfo.packageName;
+                final PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps != null) {
-                    // Try to get the status from User settings first
-                    long packedStatus = getDomainVerificationStatusLPr(ps, userId);
-                    int status = (int) (packedStatus >> 32);
-                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+                    // only check domain verification status if the app is not a browser
+                    if (!info.handleAllWebDataURI) {
+                        // Try to get the status from User settings first
+                        final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+                        final int status = (int) (packedStatus >> 32);
+                        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
                             || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
-                        if (DEBUG_EPHEMERAL) {
-                            Slog.v(TAG, "DENY ephemeral apps;"
-                                + " pkg: " + packageName + ", status: " + status);
+                            if (DEBUG_EPHEMERAL) {
+                                Slog.v(TAG, "DENY instant app;"
+                                    + " pkg: " + packageName + ", status: " + status);
+                            }
+                            return false;
                         }
-                        return false;
                     }
                     if (ps.getInstantApp(userId)) {
                         if (DEBUG_EPHEMERAL) {
@@ -13024,7 +13057,18 @@
         intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
         IActivityManager am = ActivityManager.getService();
         if (am != null) {
+            int dcsUid = -1;
+            synchronized (mPackages) {
+                if (!mDefaultContainerWhitelisted) {
+                    mDefaultContainerWhitelisted = true;
+                    PackageSetting ps = mSettings.mPackages.get(DEFAULT_CONTAINER_PACKAGE);
+                    dcsUid = UserHandle.getUid(UserHandle.USER_SYSTEM, ps.appId);
+                }
+            }
             try {
+                if (dcsUid > 0) {
+                    am.backgroundWhitelistUid(dcsUid);
+                }
                 am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(),
                         UserHandle.USER_SYSTEM);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
index ae1d50f..772fc26 100644
--- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java
+++ b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
@@ -95,20 +95,15 @@
      * Creates and Destroys the virtual display depending on the current state of VrMode.
      */
     private void updateVirtualDisplay() {
-        boolean createVirtualDisplay = "true".equals(SystemProperties.get("vr_virtualdisplay"));
         if (DEBUG) {
-            Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", createVD: " + createVirtualDisplay +
-                    ", override: " + mIsVrModeOverrideEnabled);
+            Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", override: " + mIsVrModeOverrideEnabled);
         }
 
-        if (mIsVrModeEnabled || (createVirtualDisplay && mIsVrModeOverrideEnabled)) {
+        if (mIsVrModeEnabled || mIsVrModeOverrideEnabled) {
             // TODO: Consider not creating the display until ActivityManager needs one on
             // which to display a 2D application.
-            // TODO: STOPSHIP Remove createVirtualDisplay conditional before launching.
-            if (createVirtualDisplay) {
-                startVirtualDisplay();
-                startImageReader();
-            }
+            startVirtualDisplay();
+            startImageReader();
         } else {
             // Stop virtual display to test exit condition
             stopVirtualDisplay();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 1decf4e..a8664a5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -505,6 +505,13 @@
             getController().removeStartingWindow();
         }
 
+        // If this window was animating, then we need to ensure that the app transition notifies
+        // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so
+        // add to that list now
+        if (mAppAnimator.animating) {
+            mService.mNoAnimationNotifyOnTransitionFinished.add(token);
+        }
+
         final TaskStack stack = getTask().mStack;
         if (delayed && !isEmpty()) {
             // set the token aside because it has an active animation to be finished
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 9f0ed21..7b8057ca 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -61,17 +61,21 @@
             extends WindowManagerInternal.AppTransitionListener implements Runnable {
 
         public void onAppTransitionCancelledLocked() {
+            if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:"
+                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
             animationFinished();
         }
         public void onAppTransitionFinishedLocked(IBinder token) {
+            if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:"
+                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
             animationFinished();
         }
         private void animationFinished() {
             if (mFinishAnimationAfterTransition) {
                 mHandler.removeCallbacks(this);
-                // This might end up calling into activity manager which will be bad since we have the
-                // window manager lock held at this point. Post a message to take care of the processing
-                // so we don't deadlock.
+                // This might end up calling into activity manager which will be bad since we have
+                // the window manager lock held at this point. Post a message to take care of the
+                // processing so we don't deadlock.
                 mHandler.post(this);
             }
         }
@@ -195,6 +199,7 @@
             if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) {
                 // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
                 // any further animation.
+                if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
                 animation.cancel();
             }
         }
@@ -203,7 +208,9 @@
         public void onAnimationEnd(Animator animation) {
             if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
                     + " mMoveToFullScreen=" + mMoveToFullScreen
-                    + " mSkipAnimationEnd=" + mSkipAnimationEnd);
+                    + " mSkipAnimationEnd=" + mSkipAnimationEnd
+                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
+                    + " mAppTransitionIsRunning=" + mAppTransition.isRunning());
 
             // There could be another animation running. For example in the
             // move to fullscreen case, recents will also be closing while the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 3cb96a1..ee2d5de 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -441,6 +441,8 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
             appAnimator.clearThumbnail();
             appAnimator.setNullAnimation();
+            // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
+            //       animating?
             wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a5eac46..63af2da 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -668,6 +668,7 @@
         VibratorService vibrator = null;
         IStorageManager storageManager = null;
         NetworkManagementService networkManagement = null;
+        IpSecService ipSecService = null;
         NetworkStatsService networkStats = null;
         NetworkPolicyManagerService networkPolicy = null;
         ConnectivityService connectivity = null;
@@ -1015,6 +1016,15 @@
                     reportWtf("starting NetworkManagement Service", e);
                 }
                 traceEnd();
+
+                traceBeginAndSlog("StartIpSecService");
+                try {
+                    ipSecService = IpSecService.create(context);
+                    ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
+                } catch (Throwable e) {
+                    reportWtf("starting IpSec Service", e);
+                }
+                traceEnd();
             }
 
             if (!disableNonCoreServices && !disableTextServices) {
@@ -1628,6 +1638,7 @@
         final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
         final MediaRouterService mediaRouterF = mediaRouter;
         final MmsServiceBroker mmsServiceF = mmsService;
+        final IpSecService ipSecServiceF = ipSecService;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -1691,6 +1702,13 @@
                         .networkScoreAndNetworkManagementServiceReady();
             }
             traceEnd();
+            traceBeginAndSlog("MakeIpSecServiceReady");
+            try {
+                if (ipSecServiceF != null) ipSecServiceF.systemReady();
+            } catch (Throwable e) {
+                reportWtf("making IpSec Service ready", e);
+            }
+            traceEnd();
             traceBeginAndSlog("MakeNetworkStatsServiceReady");
             try {
                 if (networkStatsF != null) networkStatsF.systemReady();
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 590bce1..61a9294 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -23,7 +23,6 @@
 import android.net.apf.ApfCapabilities;
 import android.net.apf.ApfFilter;
 import android.net.DhcpResults;
-import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -35,12 +34,10 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
 import android.net.util.MultinetworkPolicyTracker;
-import android.net.util.NetdService;
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -1030,16 +1027,14 @@
 
     private boolean startIPv6() {
         // Set privacy extensions.
-        final String PREFER_TEMPADDRS = "2";
         try {
-            NetdService.run((INetd netd) -> {
-                netd.setProcSysNet(
-                        INetd.IPV6, INetd.CONF, mInterfaceName, "use_tempaddr",
-                        PREFER_TEMPADDRS);
-            });
+            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
             mNwService.enableIpv6(mInterfaceName);
-        } catch (IllegalStateException|RemoteException|ServiceSpecificException e) {
-            logError("Unable to change interface settings: %s", e);
+        } catch (RemoteException re) {
+            logError("Unable to change interface settings: %s", re);
+            return false;
+        } catch (IllegalStateException ie) {
+            logError("Unable to change interface settings: %s", ie);
             return false;
         }
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 6c7f146..205c8de 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -52,20 +52,6 @@
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <service android:name="com.android.server.AccessibilityManagerServiceTest$MyFirstMockAccessibilityService"
-            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
-          <intent-filter>
-            <action android:name="android.accessibilityservice.AccessibilityService"/>
-          </intent-filter>
-        </service>
-
-        <service android:name="com.android.server.AccessibilityManagerServiceTest$MySecondMockAccessibilityService"
-            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
-          <intent-filter>
-            <action android:name="android.accessibilityservice.AccessibilityService"/>
-          </intent-filter>
-        </service>
-
         <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
             android:exported="false">
           <intent-filter>
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
deleted file mode 100644
index 340c624..0000000
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ServiceInfo;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-import android.view.accessibility.IAccessibilityManagerClient;
-
-import com.android.internal.util.IntPair;
-
-/**
- * This test exercises the
- * {@link com.android.server.accessibility.AccessibilityManagerService} by mocking the
- * {@link android.view.accessibility.AccessibilityManager} which talks to to the
- * service. The service itself is interacting with the platform. Note: Testing
- * the service in full isolation would require significant amount of work for
- * mocking all system interactions. It would also require a lot of mocking code.
- */
-public class AccessibilityManagerServiceTest extends AndroidTestCase {
-
-    /**
-     * Timeout required for pending Binder calls or event processing to
-     * complete.
-     */
-    private static final long TIMEOUT_BINDER_CALL = 100;
-
-    /**
-     * Timeout in which we are waiting for the system to start the mock
-     * accessibility services.
-     */
-    private static final long TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES = 1000;
-
-    /**
-     * Timeout used for testing that a service is notified only upon a
-     * notification timeout.
-     */
-    private static final long TIMEOUT_TEST_NOTIFICATION_TIMEOUT = 300;
-
-    /**
-     * The interface used to talk to the tested service.
-     */
-    private IAccessibilityManager mManagerService;
-
-    @Override
-    protected void setUp() throws Exception {
-        // Reset the state.
-        ensureOnlyMockServicesEnabled(getContext(), false, false);
-    }
-
-    @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-        if (MyFirstMockAccessibilityService.sComponentName == null) {
-            MyFirstMockAccessibilityService.sComponentName = new ComponentName(
-                    context.getPackageName(), MyFirstMockAccessibilityService.class.getName())
-                    .flattenToShortString();
-        }
-        if (MySecondMockAccessibilityService.sComponentName == null) {
-            MySecondMockAccessibilityService.sComponentName = new ComponentName(
-                    context.getPackageName(), MySecondMockAccessibilityService.class.getName())
-                    .flattenToShortString();
-        }
-    }
-
-    /**
-     * Creates a new instance.
-     */
-    public AccessibilityManagerServiceTest() {
-        IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
-        mManagerService = IAccessibilityManager.Stub.asInterface(iBinder);
-    }
-
-    @LargeTest
-    public void testAddClient_AccessibilityDisabledThenEnabled() throws Exception {
-        // at least some service must be enabled, otherwise accessibility will always be disabled.
-        ensureOnlyMockServicesEnabled(mContext, true, false);
-
-        // make sure accessibility is disabled
-        ensureAccessibilityEnabled(mContext, false);
-
-        // create a client mock instance
-        MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
-
-        // invoke the method under test
-        final int stateFlagsDisabled =
-                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
-        boolean enabledAccessibilityDisabled =
-            (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
-
-        // check expected result
-        assertFalse("The client must be disabled since accessibility is disabled.",
-                enabledAccessibilityDisabled);
-
-        // enable accessibility
-        ensureAccessibilityEnabled(mContext, true);
-
-        // invoke the method under test
-        final int stateFlagsEnabled =
-                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
-        boolean enabledAccessibilityEnabled =
-            (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
-
-        // check expected result
-        assertTrue("The client must be enabled since accessibility is enabled.",
-                enabledAccessibilityEnabled);
-    }
-
-    @LargeTest
-    public void testAddClient_AccessibilityEnabledThenDisabled() throws Exception {
-        // at least some service must be enabled, otherwise accessibility will always be disabled.
-        ensureOnlyMockServicesEnabled(mContext, true, false);
-
-        // enable accessibility before registering the client
-        ensureAccessibilityEnabled(mContext, true);
-
-        // create a client mock instance
-        MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
-
-        // invoke the method under test
-        final int stateFlagsEnabled =
-                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
-        boolean enabledAccessibilityEnabled =
-            (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
-
-        // check expected result
-        assertTrue("The client must be enabled since accessibility is enabled.",
-                enabledAccessibilityEnabled);
-
-        // disable accessibility
-        ensureAccessibilityEnabled(mContext, false);
-
-        // invoke the method under test
-        final int stateFlagsDisabled =
-                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
-        boolean enabledAccessibilityDisabled =
-            (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
-
-        // check expected result
-        assertFalse("The client must be disabled since accessibility is disabled.",
-                enabledAccessibilityDisabled);
-    }
-
-    @LargeTest
-    public void testGetAccessibilityServicesList() throws Exception {
-        boolean firstMockServiceInstalled = false;
-        boolean secondMockServiceInstalled = false;
-
-        String packageName = getContext().getPackageName();
-        String firstMockServiceClassName = MyFirstMockAccessibilityService.class.getName();
-        String secondMockServiceClassName = MySecondMockAccessibilityService.class.getName();
-
-        // look for the two mock services
-        for (AccessibilityServiceInfo info : mManagerService.getInstalledAccessibilityServiceList(
-                UserHandle.USER_CURRENT)) {
-            ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
-            if (packageName.equals(serviceInfo.packageName)) {
-                if (firstMockServiceClassName.equals(serviceInfo.name)) {
-                    firstMockServiceInstalled = true;
-                } else if (secondMockServiceClassName.equals(serviceInfo.name)) {
-                    secondMockServiceInstalled = true;
-                }
-            }
-        }
-
-        // check expected result
-        assertTrue("First mock service must be installed", firstMockServiceInstalled);
-        assertTrue("Second mock service must be installed", secondMockServiceInstalled);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_OneService_MatchingPackageAndEventType()
-            throws Exception {
-        // enable the mock accessibility service
-        ensureOnlyMockServicesEnabled(mContext, true, false);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the mock service
-        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
-        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // wait for the binder call to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-
-        // set expectations
-        service.expectEvent(sentEvent);
-        service.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_OneService_NotMatchingPackage() throws Exception {
-        // enable the mock accessibility service
-        ensureOnlyMockServicesEnabled(mContext, true, false);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the mock service
-        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
-        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // wait for the binder call to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-        sentEvent.setPackageName("no.service.registered.for.this.package");
-
-        // set expectations
-        service.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_OneService_NotMatchingEventType() throws Exception {
-        // enable the mock accessibility service
-        ensureOnlyMockServicesEnabled(mContext, true, false);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the mock service
-        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
-        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // wait for the binder call to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-        sentEvent.setEventType(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
-
-        // set expectations
-        service.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_OneService_NotificationAfterTimeout() throws Exception {
-        // enable the mock accessibility service
-        ensureOnlyMockServicesEnabled(mContext, true, false);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the mock service
-        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
-        AccessibilityServiceInfo info = MockAccessibilityService.createDefaultInfo();
-        info.notificationTimeout = TIMEOUT_TEST_NOTIFICATION_TIMEOUT;
-        service.setServiceInfo(info);
-
-        // wait for the binder call to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate the first event to be sent
-        AccessibilityEvent firstEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(firstEvent);
-
-        // create and populate the second event to be sent
-        AccessibilityEvent secondEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(secondEvent);
-
-        // set expectations
-        service.expectEvent(secondEvent);
-        service.replay();
-
-        // send the events
-        mManagerService.sendAccessibilityEvent(firstEvent, UserHandle.USER_CURRENT);
-        mManagerService.sendAccessibilityEvent(secondEvent, UserHandle.USER_CURRENT);
-
-        // wait for #sendAccessibilityEvent to reach the backing service
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        try {
-            service.verify();
-            fail("No events must be dispatched before the expiration of the notification timeout.");
-        } catch (IllegalStateException ise) {
-            /* expected */
-        }
-
-        // wait for the configured notification timeout to expire
-        Thread.sleep(TIMEOUT_TEST_NOTIFICATION_TIMEOUT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_DiffFeedback()
-            throws Exception {
-        // enable the mock accessibility services
-        ensureOnlyMockServicesEnabled(mContext, true, true);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the first mock service
-        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
-        AccessibilityServiceInfo firstInfo = MockAccessibilityService.createDefaultInfo();
-        firstInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
-        firstService.setServiceInfo(firstInfo);
-
-        // configure the second mock service
-        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
-        AccessibilityServiceInfo secondInfo = MockAccessibilityService.createDefaultInfo();
-        secondInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_HAPTIC;
-        secondService.setServiceInfo(secondInfo);
-
-        // wait for the binder calls to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-
-        // set expectations for the first mock service
-        firstService.expectEvent(sentEvent);
-        firstService.replay();
-
-        // set expectations for the second mock service
-        secondService.expectEvent(sentEvent);
-        secondService.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(firstService);
-        assertMockServiceVerifiedWithinTimeout(secondService);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType()
-            throws Exception {
-        // enable the mock accessibility services
-        ensureOnlyMockServicesEnabled(mContext, true, true);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the first mock service
-        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
-        firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // configure the second mock service
-        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
-        secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // wait for the binder calls to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-
-        // set expectations for the first mock service
-        firstService.expectEvent(sentEvent);
-        firstService.replay();
-
-        // set expectations for the second mock service
-        secondService.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(firstService);
-        assertMockServiceVerifiedWithinTimeout(secondService);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_OneDefault()
-            throws Exception {
-        // enable the mock accessibility services
-        ensureOnlyMockServicesEnabled(mContext, true, true);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the first mock service
-        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
-        AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
-        firstInfo.flags = AccessibilityServiceInfo.DEFAULT;
-        firstService.setServiceInfo(firstInfo);
-
-        // configure the second mock service
-        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
-        secondService.setServiceInfo(MySecondMockAccessibilityService.createDefaultInfo());
-
-        // wait for the binder calls to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-
-        // set expectations for the first mock service
-        firstService.replay();
-
-        // set expectations for the second mock service
-        secondService.expectEvent(sentEvent);
-        secondService.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(firstService);
-        assertMockServiceVerifiedWithinTimeout(secondService);
-    }
-
-    @LargeTest
-    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_TwoDefault()
-            throws Exception {
-        // enable the mock accessibility services
-        ensureOnlyMockServicesEnabled(mContext, true, true);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the first mock service
-        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
-        AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
-        firstInfo.flags = AccessibilityServiceInfo.DEFAULT;
-        firstService.setServiceInfo(firstInfo);
-
-        // configure the second mock service
-        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
-        AccessibilityServiceInfo secondInfo = MyFirstMockAccessibilityService.createDefaultInfo();
-        secondInfo.flags = AccessibilityServiceInfo.DEFAULT;
-        secondService.setServiceInfo(firstInfo);
-
-        // wait for the binder calls to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // create and populate an event to be sent
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-        fullyPopulateDefaultAccessibilityEvent(sentEvent);
-
-        // set expectations for the first mock service
-        firstService.expectEvent(sentEvent);
-        firstService.replay();
-
-        // set expectations for the second mock service
-        secondService.replay();
-
-        // send the event
-        mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(firstService);
-        assertMockServiceVerifiedWithinTimeout(secondService);
-    }
-
-    @LargeTest
-    public void testInterrupt() throws Exception {
-        // enable the mock accessibility services
-        ensureOnlyMockServicesEnabled(mContext, true, true);
-
-        // set the accessibility setting value
-        ensureAccessibilityEnabled(mContext, true);
-
-        // configure the first mock service
-        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
-        firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // configure the second mock service
-        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
-        secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
-
-        // wait for the binder calls to #setService to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // set expectations for the first mock service
-        firstService.expectInterrupt();
-        firstService.replay();
-
-        // set expectations for the second mock service
-        secondService.expectInterrupt();
-        secondService.replay();
-
-        // call the method under test
-        mManagerService.interrupt(UserHandle.USER_CURRENT);
-
-        // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(firstService);
-        assertMockServiceVerifiedWithinTimeout(secondService);
-    }
-
-    /**
-     * Fully populates the {@link AccessibilityEvent} to marshal.
-     *
-     * @param sentEvent The event to populate.
-     */
-    private void fullyPopulateDefaultAccessibilityEvent(AccessibilityEvent sentEvent) {
-        sentEvent.setAddedCount(1);
-        sentEvent.setBeforeText("BeforeText");
-        sentEvent.setChecked(true);
-        sentEvent.setClassName("foo.bar.baz.Class");
-        sentEvent.setContentDescription("ContentDescription");
-        sentEvent.setCurrentItemIndex(1);
-        sentEvent.setEnabled(true);
-        sentEvent.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
-        sentEvent.setEventTime(1000);
-        sentEvent.setFromIndex(1);
-        sentEvent.setFullScreen(true);
-        sentEvent.setItemCount(1);
-        sentEvent.setPackageName("foo.bar.baz");
-        sentEvent.setParcelableData(Message.obtain(null, 1, null));
-        sentEvent.setPassword(true);
-        sentEvent.setRemovedCount(1);
-    }
-
-    /**
-     * This class is a mock {@link IAccessibilityManagerClient}.
-     */
-    public class MyMockAccessibilityManagerClient extends IAccessibilityManagerClient.Stub {
-        int mState;
-
-        public void setState(int state) {
-            mState = state;
-        }
-
-        public void notifyServicesStateChanged() {}
-
-        public void setRelevantEventTypes(int eventTypes) {}
-
-        public void setTouchExplorationEnabled(boolean enabled) {}
-    }
-
-    /**
-     * Ensures accessibility is in a given state by writing the state to the
-     * settings and waiting until the accessibility manager service pick it up.
-     *
-     * @param context A context handle to access the settings.
-     * @param enabled The accessibility state to write to the settings.
-     * @throws Exception If any error occurs.
-     */
-    private void ensureAccessibilityEnabled(Context context, boolean enabled) throws Exception {
-        boolean isEnabled = Settings.Secure.getInt(context.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
-
-        if (isEnabled == enabled) {
-            return;
-        }
-
-        Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED,
-                enabled ? 1 : 0);
-
-        // wait the accessibility manager service to pick the change up
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-    }
-
-    /**
-     * Ensures the only {@link MockAccessibilityService}s with given component
-     * names are enabled by writing to the system settings and waiting until the
-     * accessibility manager service picks that up or the
-     * {@link #TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES} is exceeded.
-     *
-     * @param context A context handle to access the settings.
-     * @param firstMockServiceEnabled If the first mock accessibility service is enabled.
-     * @param secondMockServiceEnabled If the second mock accessibility service is enabled.
-     * @throws IllegalStateException If some of the requested for enabling mock services
-     *         is not properly started.
-     * @throws Exception Exception If any error occurs.
-     */
-    private void ensureOnlyMockServicesEnabled(Context context, boolean firstMockServiceEnabled,
-            boolean secondMockServiceEnabled) throws Exception {
-        String enabledServices = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
-
-        StringBuilder servicesToEnable = new StringBuilder();
-        if (firstMockServiceEnabled) {
-            servicesToEnable.append(MyFirstMockAccessibilityService.sComponentName).append(":");
-        }
-        if (secondMockServiceEnabled) {
-            servicesToEnable.append(MySecondMockAccessibilityService.sComponentName).append(":");
-        }
-
-        Settings.Secure.putString(context.getContentResolver(),
-                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, servicesToEnable.toString());
-
-        // Optimization. If things will not change, we don't have to do anything.
-        if (servicesToEnable.equals(enabledServices)) {
-            return;
-        }
-
-        // we have enabled the services of interest and need to wait until they
-        // are instantiated and started (if needed) and the system binds to them
-        boolean firstMockServiceOK = false;
-        boolean secondMockServiceOK = false;
-        long start = SystemClock.uptimeMillis();
-        long pollingInterval = TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES / 6;
-
-        while (SystemClock.uptimeMillis() - start < TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES)  {
-            firstMockServiceOK = !firstMockServiceEnabled
-                    || (MyFirstMockAccessibilityService.sInstance != null
-                    && MyFirstMockAccessibilityService.sInstance.isSystemBoundAsClient());
-
-            secondMockServiceOK = !secondMockServiceEnabled
-                    || (MySecondMockAccessibilityService.sInstance != null
-                    && MySecondMockAccessibilityService.sInstance.isSystemBoundAsClient());
-
-            if (firstMockServiceOK && secondMockServiceOK) {
-                return;
-            }
-
-            Thread.sleep(pollingInterval);
-        }
-
-        StringBuilder message = new StringBuilder();
-        message.append("Mock accessibility services not started or system not bound as a client: ");
-        if (!firstMockServiceOK) {
-            message.append(MyFirstMockAccessibilityService.sComponentName);
-            message.append(" ");
-        }
-        if (!secondMockServiceOK) {
-            message.append(MySecondMockAccessibilityService.sComponentName);
-        }
-        throw new IllegalStateException(message.toString());
-    }
-
-    /**
-     * Asserts the the mock accessibility service has been successfully verified
-     * (which is it has received the expected method calls with expected
-     * arguments) within the {@link #TIMEOUT_BINDER_CALL}. The verified state is
-     * checked by polling upon small intervals.
-     *
-     * @param service The service to verify.
-     * @throws Exception If the verification has failed with exception after the
-     *             {@link #TIMEOUT_BINDER_CALL}.
-     */
-    private void assertMockServiceVerifiedWithinTimeout(MockAccessibilityService service)
-            throws Exception {
-        Exception lastVerifyException = null;
-        long beginTime = SystemClock.uptimeMillis();
-        long pollTimeout = TIMEOUT_BINDER_CALL / 5;
-
-        // poll until the timeout has elapsed
-        while (SystemClock.uptimeMillis() - beginTime < TIMEOUT_BINDER_CALL) {
-            // sleep first since immediate call will always fail
-            try {
-                Thread.sleep(pollTimeout);
-            } catch (InterruptedException ie) {
-                /* ignore */
-            }
-            // poll for verification and if this fails save the exception and
-            // keep polling
-            try {
-                service.verify();
-                // reset so it does not accept more events
-                service.reset();
-                return;
-            } catch (Exception e) {
-                lastVerifyException = e;
-            }
-        }
-
-        // reset, we have already failed
-        service.reset();
-
-        // always not null
-        throw lastVerifyException;
-    }
-
-    /**
-     * This class is the first mock {@link AccessibilityService}.
-     */
-    public static class MyFirstMockAccessibilityService extends MockAccessibilityService {
-
-        /**
-         * The service {@link ComponentName} flattened as a string.
-         */
-        static String sComponentName;
-
-        /**
-         * Handle to the service instance.
-         */
-        static MyFirstMockAccessibilityService sInstance;
-
-        /**
-         * Creates a new instance.
-         */
-        public MyFirstMockAccessibilityService() {
-            sInstance = this;
-        }
-    }
-
-    /**
-     * This class is the first mock {@link AccessibilityService}.
-     */
-    public static class MySecondMockAccessibilityService extends MockAccessibilityService {
-
-        /**
-         * The service {@link ComponentName} flattened as a string.
-         */
-        static String sComponentName;
-
-        /**
-         * Handle to the service instance.
-         */
-        static MySecondMockAccessibilityService sInstance;
-
-        /**
-         * Creates a new instance.
-         */
-        public MySecondMockAccessibilityService() {
-            sInstance = this;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
deleted file mode 100644
index 9261771..0000000
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.os.UserHandle;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-import android.view.accessibility.IAccessibilityManagerClient;
-
-import com.android.internal.util.IntPair;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for the AccessibilityManager which mocking the backing service.
- */
-public class AccessibilityManagerTest extends AndroidTestCase {
-
-    /**
-     * Timeout required for pending Binder calls or event processing to
-     * complete.
-     */
-    public static final long TIMEOUT_BINDER_CALL = 50;
-
-    @Mock
-    private IAccessibilityManager mMockService;
-
-    @Override
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    private AccessibilityManager createManager(boolean enabled) throws Exception {
-        if (enabled) {
-            when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
-                    .thenReturn(
-                            IntPair.of(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
-                                    AccessibilityEvent.TYPES_ALL_MASK));
-        } else {
-            when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
-                    .thenReturn(IntPair.of(0, AccessibilityEvent.TYPES_ALL_MASK));
-        }
-
-        AccessibilityManager manager =
-                new AccessibilityManager(mContext, mMockService, UserHandle.USER_CURRENT);
-
-        verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt());
-
-        return manager;
-    }
-
-    @MediumTest
-    public void testGetAccessibilityServiceList() throws Exception {
-        // create a list of installed accessibility services the mock service returns
-        List<AccessibilityServiceInfo> expectedServices = new ArrayList<>();
-        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
-        accessibilityServiceInfo.packageNames = new String[] { "foo.bar" };
-        expectedServices.add(accessibilityServiceInfo);
-
-        // configure the mock service behavior
-        when(mMockService.getInstalledAccessibilityServiceList(anyInt()))
-                .thenReturn(expectedServices);
-
-        // invoke the method under test
-        AccessibilityManager manager = createManager(true);
-        List<AccessibilityServiceInfo> receivedServices =
-                manager.getInstalledAccessibilityServiceList();
-
-        verify(mMockService).getInstalledAccessibilityServiceList(UserHandle.USER_CURRENT);
-        // check expected result (list equals() compares it contents as well)
-        assertEquals("All expected services must be returned", expectedServices, receivedServices);
-    }
-
-    @MediumTest
-    public void testInterrupt() throws Exception {
-        AccessibilityManager manager = createManager(true);
-        manager.interrupt();
-
-        verify(mMockService).interrupt(UserHandle.USER_CURRENT);
-    }
-
-    @LargeTest
-    public void testIsEnabled() throws Exception {
-        // invoke the method under test
-        AccessibilityManager manager = createManager(true);
-        boolean isEnabledServiceEnabled = manager.isEnabled();
-
-        // check expected result
-        assertTrue("Must be enabled since the mock service is enabled", isEnabledServiceEnabled);
-
-        // disable accessibility
-        manager.getClient().setState(0);
-
-        // wait for the asynchronous IBinder call to complete
-        Thread.sleep(TIMEOUT_BINDER_CALL);
-
-        // invoke the method under test
-        boolean isEnabledServcieDisabled = manager.isEnabled();
-
-        // check expected result
-        assertFalse("Must be disabled since the mock service is disabled",
-                isEnabledServcieDisabled);
-    }
-
-    @MediumTest
-    public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-
-        AccessibilityManager manager = createManager(true);
-        manager.sendAccessibilityEvent(sentEvent);
-
-        assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain());
-    }
-
-    @MediumTest
-    public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
-        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
-
-        AccessibilityManager manager = createManager(false  /* disabled */);
-
-        try {
-            manager.sendAccessibilityEvent(sentEvent);
-            fail("No accessibility events are sent if accessibility is disabled");
-        } catch (IllegalStateException ise) {
-            // check expected result
-            assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java b/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
deleted file mode 100644
index e1c5cee..0000000
--- a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.Intent;
-import android.os.Message;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-
-import junit.framework.TestCase;
-
-/**
- * This is the base class for mock {@link AccessibilityService}s.
- */
-public abstract class MockAccessibilityService extends AccessibilityService {
-
-    /**
-     * The event this service expects to receive.
-     */
-    private final Queue<AccessibilityEvent> mExpectedEvents = new LinkedList<AccessibilityEvent>();
-
-    /**
-     * Interruption call this service expects to receive.
-     */
-    private boolean mExpectedInterrupt;
-
-    /**
-     * Flag if the mock is currently replaying.
-     */
-    private boolean mReplaying;
-
-    /**
-     * Flag if the system is bound as a client to this service.
-     */
-    private boolean mIsSystemBoundAsClient;
-
-    /**
-     * Creates an {@link AccessibilityServiceInfo} populated with default
-     * values.
-     *
-     * @return The default info.
-     */
-    public static AccessibilityServiceInfo createDefaultInfo() {
-        AccessibilityServiceInfo defaultInfo = new AccessibilityServiceInfo();
-        defaultInfo.eventTypes = AccessibilityEvent.TYPE_ANNOUNCEMENT;
-        defaultInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
-        defaultInfo.flags = 0;
-        defaultInfo.notificationTimeout = 0;
-        defaultInfo.packageNames = new String[] {
-            "foo.bar.baz"
-        };
-
-        return defaultInfo;
-    }
-
-    /**
-     * Starts replaying the mock.
-     */
-    public void replay() {
-        mReplaying = true;
-    }
-
-    /**
-     * Verifies if all expected service methods have been called.
-     */
-    public void verify() {
-        if (!mReplaying) {
-            throw new IllegalStateException("Did you forget to call replay()");
-        }
-
-        if (mExpectedInterrupt) {
-            throw new IllegalStateException("Expected call to #interrupt() not received");
-        }
-        if (!mExpectedEvents.isEmpty()) {
-            throw new IllegalStateException("Expected a call to onAccessibilityEvent() for "
-                    + "events \"" + mExpectedEvents + "\" not received");
-        }
-    }
-
-    /**
-     * Resets this instance so it can be reused.
-     */
-    public void reset() {
-        mExpectedEvents.clear();
-        mExpectedInterrupt = false;
-        mReplaying = false;
-    }
-
-    /**
-     * Sets an expected call to
-     * {@link #onAccessibilityEvent(AccessibilityEvent)} with given event as
-     * argument.
-     *
-     * @param expectedEvent The expected event argument.
-     */
-    public void expectEvent(AccessibilityEvent expectedEvent) {
-        mExpectedEvents.add(expectedEvent);
-    }
-
-    /**
-     * Sets an expected call of {@link #onInterrupt()}.
-     */
-    public void expectInterrupt() {
-        mExpectedInterrupt = true;
-    }
-
-    @Override
-    public void onAccessibilityEvent(AccessibilityEvent receivedEvent) {
-        if (!mReplaying) {
-            return;
-        }
-
-        if (mExpectedEvents.isEmpty()) {
-            throw new IllegalStateException("Unexpected event: " + receivedEvent);
-        }
-
-        AccessibilityEvent expectedEvent = mExpectedEvents.poll();
-        assertEqualsAccessiblityEvent(expectedEvent, receivedEvent);
-    }
-
-    @Override
-    public void onInterrupt() {
-        if (!mReplaying) {
-            return;
-        }
-
-        if (!mExpectedInterrupt) {
-            throw new IllegalStateException("Unexpected call to onInterrupt()");
-        }
-
-        mExpectedInterrupt = false;
-    }
-
-    @Override
-    protected void onServiceConnected() {
-        mIsSystemBoundAsClient = true;
-    }
-
-    @Override
-    public boolean onUnbind(Intent intent) {
-        mIsSystemBoundAsClient = false;
-        return false;
-    }
-
-    /**
-     * Returns if the system is bound as client to this service.
-     *
-     * @return True if the system is bound, false otherwise.
-     */
-    public boolean isSystemBoundAsClient() {
-        return mIsSystemBoundAsClient;
-    }
-
-    /**
-     * Compares all properties of the <code>expectedEvent</code> and the
-     * <code>receviedEvent</code> to verify that the received event is the one
-     * that is expected.
-     */
-    private void assertEqualsAccessiblityEvent(AccessibilityEvent expectedEvent,
-            AccessibilityEvent receivedEvent) {
-        TestCase.assertEquals("addedCount has incorrect value", expectedEvent.getAddedCount(),
-                receivedEvent.getAddedCount());
-        TestCase.assertEquals("beforeText has incorrect value", expectedEvent.getBeforeText(),
-                receivedEvent.getBeforeText());
-        TestCase.assertEquals("checked has incorrect value", expectedEvent.isChecked(),
-                receivedEvent.isChecked());
-        TestCase.assertEquals("className has incorrect value", expectedEvent.getClassName(),
-                receivedEvent.getClassName());
-        TestCase.assertEquals("contentDescription has incorrect value", expectedEvent
-                .getContentDescription(), receivedEvent.getContentDescription());
-        TestCase.assertEquals("currentItemIndex has incorrect value", expectedEvent
-                .getCurrentItemIndex(), receivedEvent.getCurrentItemIndex());
-        TestCase.assertEquals("enabled has incorrect value", expectedEvent.isEnabled(),
-                receivedEvent.isEnabled());
-        TestCase.assertEquals("eventType has incorrect value", expectedEvent.getEventType(),
-                receivedEvent.getEventType());
-        TestCase.assertEquals("fromIndex has incorrect value", expectedEvent.getFromIndex(),
-                receivedEvent.getFromIndex());
-        TestCase.assertEquals("fullScreen has incorrect value", expectedEvent.isFullScreen(),
-                receivedEvent.isFullScreen());
-        TestCase.assertEquals("itemCount has incorrect value", expectedEvent.getItemCount(),
-                receivedEvent.getItemCount());
-        assertEqualsNotificationAsParcelableData(expectedEvent, receivedEvent);
-        TestCase.assertEquals("password has incorrect value", expectedEvent.isPassword(),
-                receivedEvent.isPassword());
-        TestCase.assertEquals("removedCount has incorrect value", expectedEvent.getRemovedCount(),
-                receivedEvent.getRemovedCount());
-        assertEqualsText(expectedEvent, receivedEvent);
-    }
-
-    /**
-     * Compares the {@link android.os.Parcelable} data of the
-     * <code>expectedEvent</code> and <code>receivedEvent</code> to verify that
-     * the received event is the one that is expected.
-     */
-    private void assertEqualsNotificationAsParcelableData(AccessibilityEvent expectedEvent,
-            AccessibilityEvent receivedEvent) {
-        String message = "parcelableData has incorrect value";
-        Message expectedMessage = (Message) expectedEvent.getParcelableData();
-        Message receivedMessage = (Message) receivedEvent.getParcelableData();
-
-        if (expectedMessage == null) {
-            if (receivedMessage == null) {
-                return;
-            }
-        }
-
-        TestCase.assertNotNull(message, receivedMessage);
-
-        // we do a very simple sanity check since we do not test Message
-        TestCase.assertEquals(message, expectedMessage.what, receivedMessage.what);
-    }
-
-    /**
-     * Compares the text of the <code>expectedEvent</code> and
-     * <code>receivedEvent</code> by comparing the string representation of the
-     * corresponding {@link CharSequence}s.
-     */
-    private void assertEqualsText(AccessibilityEvent expectedEvent,
-            AccessibilityEvent receivedEvent) {
-        String message = "text has incorrect value";
-        List<CharSequence> expectedText = expectedEvent.getText();
-        List<CharSequence> receivedText = receivedEvent.getText();
-
-        TestCase.assertEquals(message, expectedText.size(), receivedText.size());
-
-        Iterator<CharSequence> expectedTextIterator = expectedText.iterator();
-        Iterator<CharSequence> receivedTextIterator = receivedText.iterator();
-
-        for (int i = 0; i < expectedText.size(); i++) {
-            // compare the string representation
-            TestCase.assertEquals(message, expectedTextIterator.next().toString(),
-                    receivedTextIterator.next().toString());
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index e0ac393..353199a 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -645,20 +645,6 @@
     }
 
     @Test
-    public void testDump_noDumpPermission() {
-        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
-                eq(permission.DUMP), anyString());
-
-        try {
-            mNetworkScoreService.dump(
-                    new FileDescriptor(), new PrintWriter(new StringWriter()), new String[0]);
-            fail("SecurityException expected");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    @Test
     public void testDump_doesNotCrash() {
         when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
         StringWriter stringWriter = new StringWriter();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
new file mode 100644
index 0000000..5d09e31
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010 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.accessibility;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertSame;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Instrumentation;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import com.android.internal.util.IntPair;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for the AccessibilityManager by mocking the backing service.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityManagerTest {
+    private static final boolean WITH_A11Y_ENABLED = true;
+    private static final boolean WITH_A11Y_DISABLED = false;
+
+    @Mock private IAccessibilityManager mMockService;
+    private MessageCapturingHandler mHandler;
+    private Instrumentation mInstrumentation;
+
+    @BeforeClass
+    public static void oneTimeInitialization() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mHandler = new MessageCapturingHandler(null);
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    private AccessibilityManager createManager(boolean enabled) throws Exception {
+        long serviceReturnValue = IntPair.of(
+                (enabled) ? AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED : 0,
+                AccessibilityEvent.TYPES_ALL_MASK);
+        when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
+                .thenReturn(serviceReturnValue);
+
+        AccessibilityManager manager =
+                new AccessibilityManager(mHandler, mMockService, UserHandle.USER_CURRENT);
+
+        verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt());
+        mHandler.setCallback(manager.getCallback());
+        mHandler.sendAllMessages();
+        return manager;
+    }
+
+    @Test
+    public void testGetAccessibilityServiceList() throws Exception {
+        // create a list of installed accessibility services the mock service returns
+        List<AccessibilityServiceInfo> expectedServices = new ArrayList<>();
+        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
+        accessibilityServiceInfo.packageNames = new String[] { "foo.bar" };
+        expectedServices.add(accessibilityServiceInfo);
+
+        // configure the mock service behavior
+        when(mMockService.getInstalledAccessibilityServiceList(anyInt()))
+                .thenReturn(expectedServices);
+
+        // invoke the method under test
+        AccessibilityManager manager = createManager(true);
+        List<AccessibilityServiceInfo> receivedServices =
+                manager.getInstalledAccessibilityServiceList();
+
+        verify(mMockService).getInstalledAccessibilityServiceList(UserHandle.USER_CURRENT);
+        // check expected result (list equals() compares it contents as well)
+        assertEquals("All expected services must be returned", expectedServices, receivedServices);
+    }
+
+    @Test
+    public void testInterrupt() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        manager.interrupt();
+
+        verify(mMockService).interrupt(UserHandle.USER_CURRENT);
+    }
+
+    @Test
+    public void testIsEnabled() throws Exception {
+        // Create manager with a11y enabled
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        assertTrue("Must be enabled since the mock service is enabled", manager.isEnabled());
+
+        // Disable accessibility
+        manager.getClient().setState(0);
+        mHandler.sendAllMessages();
+        assertFalse("Must be disabled since the mock service is disabled", manager.isEnabled());
+    }
+
+    @Test
+    public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain(
+                AccessibilityEvent.TYPE_ANNOUNCEMENT);
+
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        manager.sendAccessibilityEvent(sentEvent);
+
+        assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain());
+    }
+
+    @Test
+    public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+
+        AccessibilityManager manager = createManager(WITH_A11Y_DISABLED);
+        mInstrumentation.runOnMainSync(() -> {
+            try {
+                manager.sendAccessibilityEvent(sentEvent);
+                fail("No accessibility events are sent if accessibility is disabled");
+            } catch (IllegalStateException ise) {
+                // check expected result
+                assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
+            }
+        });
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index d44c1ca..5887215 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -20,11 +20,12 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -48,6 +49,7 @@
 import android.view.WindowManagerInternal.MagnificationCallbacks;
 
 import com.android.internal.R;
+
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
@@ -471,9 +473,10 @@
     public void testResetIfNeeded_doesWhatItSays() {
         mMagnificationController.register();
         zoomIn2xToMiddle();
+        reset(mMockAms);
         assertTrue(mMagnificationController.resetIfNeeded(false));
         verify(mMockAms).notifyMagnificationChanged(
-                eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyInt(), anyInt());
+                eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
         assertFalse(mMagnificationController.isMagnifying());
         assertFalse(mMagnificationController.resetIfNeeded(false));
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
index 003f7ab..0dba35f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
@@ -42,6 +42,10 @@
         return super.sendMessageAtTime(message, uptimeMillis);
     }
 
+    public void setCallback(Handler.Callback callback) {
+        mCallback = callback;
+    }
+
     public void sendOneMessage() {
         Message message = timedMessages.remove(0).first;
         removeMessages(message.what, message.obj);
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index aa37407..5d0c23f 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -149,7 +149,7 @@
         // 2nd account
         Account account2 = new Account("name", "example2.com");
         long accId2 = mAccountsDb.insertCeAccount(account2, "password");
-        mAccountsDb.insertDeAccount(account2, accId);
+        mAccountsDb.insertDeAccount(account2, accId2);
         mAccountsDb.insertAuthToken(accId2, "type", "token");
 
         mAccountsDb.deleteAuthTokensByAccountId(accId2);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index af3201c..54ecab3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -33,8 +33,7 @@
  *  bit FrameworksServicesTests:com.android.server.am.ActivityRecordTests
  */
 @MediumTest
-// TODO(b/36916522): Currently failing in CI.
-// @Presubmit
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityRecordTests extends ActivityTestsBase {
     private final ComponentName testActivityComponent =
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
new file mode 100644
index 0000000..8423aff
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -0,0 +1,52 @@
+/*
+ * 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.am;
+
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStackSupervisorTests extends ActivityTestsBase {
+    /**
+     * This test ensures that we do not try to restore a task based off an invalid task id. The
+     * stack supervisor is a test version so there will be no tasks present. We should expect
+     * {@code null} to be returned in this case.
+     */
+    @Test
+    public void testRestoringInvalidTask() throws Exception {
+        final ActivityManagerService service = createActivityManagerService();
+        TaskRecord task = service.mStackSupervisor.anyTaskForIdLocked(0 /*taskId*/,
+                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/);
+        assertNull(task);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index c5cc2ff..5240586 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -25,7 +25,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.support.test.InstrumentationRegistry;
 import com.android.server.AttributeCache;
@@ -34,6 +34,7 @@
 
 import com.android.server.wm.WindowManagerService;
 import com.android.server.wm.WindowTestUtils;
+import org.junit.After;
 import org.junit.Before;
 import org.mockito.MockitoAnnotations;
 
@@ -42,8 +43,7 @@
  */
 public class ActivityTestsBase {
     private final Context mContext = InstrumentationRegistry.getContext();
-    private static boolean sLooperPrepared;
-    private Handler mHandler;
+    private HandlerThread mHandlerThread;
 
     // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must
     // be called at before any tests.
@@ -52,11 +52,13 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
+        mHandlerThread.start();
+    }
 
-        if (!sLooperPrepared) {
-            sLooperPrepared = true;
-            Looper.prepare();
-        }
+    @After
+    public void tearDown() {
+        mHandlerThread.quitSafely();
     }
 
     protected ActivityManagerService createActivityManagerService() {
@@ -126,7 +128,7 @@
 
         @Override
         protected ActivityStackSupervisor createStackSupervisor() {
-            return new TestActivityStackSupervisor(this, new Handler().getLooper());
+            return new TestActivityStackSupervisor(this, mHandlerThread.getLooper());
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 0f1b81e..7a4746a 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManager;
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -49,16 +50,20 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.server.am.ActivityManagerService.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG;
 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
 import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -71,9 +76,29 @@
     private UserController mUserController;
     private TestInjector mInjector;
 
+    private static final List<String> START_FOREGROUND_USER_ACTIONS =
+            Arrays.asList(
+                    Intent.ACTION_USER_STARTED,
+                    Intent.ACTION_USER_SWITCHED,
+                    Intent.ACTION_USER_STARTING);
+
+    private static final List<String> START_BACKGROUND_USER_ACTIONS =
+            Arrays.asList(
+                    Intent.ACTION_USER_STARTED,
+                    Intent.ACTION_LOCKED_BOOT_COMPLETED,
+                    Intent.ACTION_USER_STARTING);
+
+    private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES =
+            new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
+                    SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
+
+    private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES =
+            new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG));
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
+        System.setProperty("dexmaker.share_classloader", "true");
         mInjector = new TestInjector(getContext());
         mUserController = new UserController(mInjector);
         setUpUser(TEST_USER_ID, 0);
@@ -83,39 +108,62 @@
     protected void tearDown() throws Exception {
         super.tearDown();
         mInjector.handlerThread.quit();
-
     }
 
     @SmallTest
-    public void testStartUser() throws RemoteException {
-        mUserController.startUser(TEST_USER_ID, true);
+    public void testStartUser_foreground() throws RemoteException {
+        mUserController.startUser(TEST_USER_ID, true /* foreground */);
         Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
         Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
         Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
         Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
-        startUserAssertions();
+        Mockito.verify(mInjector.getActivityStackSupervisor()).setLockTaskModeLocked(
+                nullable(TaskRecord.class),
+                eq(ActivityManager.LOCK_TASK_MODE_NONE),
+                anyString(),
+                anyBoolean());
+        startForegroundUserAssertions();
+    }
+
+    @SmallTest
+    public void testStartUser_background() throws RemoteException {
+        mUserController.startUser(TEST_USER_ID, false /* foreground */);
+        Mockito.verify(
+                mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+        Mockito.verify(mInjector.getActivityStackSupervisor(), never()).setLockTaskModeLocked(
+                nullable(TaskRecord.class),
+                eq(ActivityManager.LOCK_TASK_MODE_NONE),
+                anyString(),
+                anyBoolean());
+        startBackgroundUserAssertions();
     }
 
     @SmallTest
     public void testStartUserUIDisabled() throws RemoteException {
         mUserController.mUserSwitchUiEnabled = false;
-        mUserController.startUser(TEST_USER_ID, true);
+        mUserController.startUser(TEST_USER_ID, true /* foreground */);
         Mockito.verify(mInjector.getWindowManager(), never())
                 .startFreezingScreen(anyInt(), anyInt());
         Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
         Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
-        startUserAssertions();
+        startForegroundUserAssertions();
     }
 
-    private void startUserAssertions() throws RemoteException {
-        List<String> expectedActions = Arrays.asList(Intent.ACTION_USER_STARTED,
-                Intent.ACTION_USER_SWITCHED, Intent.ACTION_USER_STARTING);
+    private void startUserAssertions(
+            List<String> expectedActions, Set<Integer> expectedMessageCodes)
+            throws RemoteException {
         assertEquals(expectedActions, getActions(mInjector.sentIntents));
-        Set<Integer> expectedCodes = new HashSet<>(
-                Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
-                        SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
-        assertEquals("Unexpected message sent", expectedCodes, actualCodes);
+        assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
+    }
+
+    private void startBackgroundUserAssertions() throws RemoteException {
+        startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
+    }
+
+    private void startForegroundUserAssertions() throws RemoteException {
+        startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
         Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
@@ -275,6 +323,7 @@
         UserManagerService userManagerMock;
         UserManagerInternal userManagerInternalMock;
         WindowManagerService windowManagerMock;
+        ActivityStackSupervisor activityStackSupervisor;
         private Context mCtx;
         List<Intent> sentIntents = new ArrayList<>();
 
@@ -287,6 +336,7 @@
             userManagerMock = mock(UserManagerService.class);
             userManagerInternalMock = mock(UserManagerInternal.class);
             windowManagerMock = mock(WindowManagerService.class);
+            activityStackSupervisor = mock(ActivityStackSupervisor.class);
         }
 
         @Override
@@ -321,12 +371,6 @@
         }
 
         @Override
-        void stackSupervisorSetLockTaskModeLocked(TaskRecord task, int lockTaskModeState,
-                String reason, boolean andResume) {
-            Log.i(TAG, "stackSupervisorSetLockTaskModeLocked");
-        }
-
-        @Override
         WindowManagerService getWindowManager() {
             return windowManagerMock;
         }
@@ -347,16 +391,15 @@
         }
 
         @Override
-        boolean stackSupervisorSwitchUserLocked(int userId, UserState uss) {
-            Log.i(TAG, "stackSupervisorSwitchUserLocked " + userId);
-            return true;
-        }
-
-        @Override
         void startHomeActivityLocked(int userId, String reason) {
             Log.i(TAG, "startHomeActivityLocked " + userId);
         }
-   }
+
+        @Override
+        ActivityStackSupervisor getActivityStackSupervisor() {
+            return activityStackSupervisor;
+        }
+    }
 
     private static class TestHandler extends Handler {
         private final List<Message> mMessages = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index f3f68ff..2663aaf 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -50,6 +50,7 @@
 
 import org.mockito.ArgumentCaptor;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
 import java.util.concurrent.CountDownLatch;
@@ -249,6 +250,25 @@
         assertEquals(7, updates.size());
     }
 
+    public void testGetInstalledProvidersForPackage() {
+        List<AppWidgetProviderInfo> allProviders = mManager.getInstalledProviders();
+        assertTrue(!allProviders.isEmpty());
+        String packageName = allProviders.get(0).provider.getPackageName();
+        List<AppWidgetProviderInfo> providersForPackage = mManager.getInstalledProvidersForPackage(
+                packageName, null);
+        // Remove providers from allProviders that don't have the given package name.
+        Iterator<AppWidgetProviderInfo> iter = allProviders.iterator();
+        while (iter.hasNext()) {
+            if (!iter.next().provider.getPackageName().equals(packageName)) {
+                iter.remove();
+            }
+        }
+        assertEquals(allProviders.size(), providersForPackage.size());
+        for (int i = 0; i < allProviders.size(); i++) {
+            assertEquals(allProviders.get(i).provider, providersForPackage.get(i).provider);
+        }
+    }
+
     private int setupHostAndWidget() {
         List<PendingHostUpdate> updates = mService.startListening(
                 mMockHost, mPkgName, HOST_ID, new int[0]).getList();
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 92233b1..f80ee73 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -356,6 +356,7 @@
         private final StatusHints mStatusHints;
         private final Bundle mExtras;
         private final Bundle mIntentExtras;
+        private final long mCreationTimeMillis;
 
         /**
          * Whether the supplied capabilities  supports the specified capability.
@@ -578,9 +579,12 @@
         }
 
         /**
-         * @return The time the {@code Call} has been connected. This information is updated
-         * periodically, but user interfaces should not rely on this to display any "call time
-         * clock".
+         * Returns the time the {@link Call} connected (i.e. became active).  This information is
+         * updated periodically, but user interfaces should not rely on this to display the "call
+         * time clock".  For the time when the call was first added to Telecom, see
+         * {@link #getCreationTimeMillis()}.
+         *
+         * @return The time the {@link Call} connected in milliseconds since the epoch.
          */
         public final long getConnectTimeMillis() {
             return mConnectTimeMillis;
@@ -622,6 +626,18 @@
             return mIntentExtras;
         }
 
+        /**
+         * Returns the time when the call was first created and added to Telecom.  This is the same
+         * time that is logged as the start time in the Call Log (see
+         * {@link android.provider.CallLog.Calls#DATE}).  To determine when the call was connected
+         * (became active), see {@link #getConnectTimeMillis()}.
+         *
+         * @return The creation time of the call, in millis since the epoch.
+         */
+        public long getCreationTimeMillis() {
+            return mCreationTimeMillis;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -641,28 +657,29 @@
                         Objects.equals(mVideoState, d.mVideoState) &&
                         Objects.equals(mStatusHints, d.mStatusHints) &&
                         areBundlesEqual(mExtras, d.mExtras) &&
-                        areBundlesEqual(mIntentExtras, d.mIntentExtras);
+                        areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
+                        Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis);
             }
             return false;
         }
 
         @Override
         public int hashCode() {
-            return
-                    Objects.hashCode(mHandle) +
-                    Objects.hashCode(mHandlePresentation) +
-                    Objects.hashCode(mCallerDisplayName) +
-                    Objects.hashCode(mCallerDisplayNamePresentation) +
-                    Objects.hashCode(mAccountHandle) +
-                    Objects.hashCode(mCallCapabilities) +
-                    Objects.hashCode(mCallProperties) +
-                    Objects.hashCode(mDisconnectCause) +
-                    Objects.hashCode(mConnectTimeMillis) +
-                    Objects.hashCode(mGatewayInfo) +
-                    Objects.hashCode(mVideoState) +
-                    Objects.hashCode(mStatusHints) +
-                    Objects.hashCode(mExtras) +
-                    Objects.hashCode(mIntentExtras);
+            return Objects.hash(mHandle,
+                            mHandlePresentation,
+                            mCallerDisplayName,
+                            mCallerDisplayNamePresentation,
+                            mAccountHandle,
+                            mCallCapabilities,
+                            mCallProperties,
+                            mDisconnectCause,
+                            mConnectTimeMillis,
+                            mGatewayInfo,
+                            mVideoState,
+                            mStatusHints,
+                            mExtras,
+                            mIntentExtras,
+                            mCreationTimeMillis);
         }
 
         /** {@hide} */
@@ -681,7 +698,8 @@
                 int videoState,
                 StatusHints statusHints,
                 Bundle extras,
-                Bundle intentExtras) {
+                Bundle intentExtras,
+                long creationTimeMillis) {
             mTelecomCallId = telecomCallId;
             mHandle = handle;
             mHandlePresentation = handlePresentation;
@@ -697,6 +715,7 @@
             mStatusHints = statusHints;
             mExtras = extras;
             mIntentExtras = intentExtras;
+            mCreationTimeMillis = creationTimeMillis;
         }
 
         /** {@hide} */
@@ -716,7 +735,8 @@
                     parcelableCall.getVideoState(),
                     parcelableCall.getStatusHints(),
                     parcelableCall.getExtras(),
-                    parcelableCall.getIntentExtras());
+                    parcelableCall.getIntentExtras(),
+                    parcelableCall.getCreationTimeMillis());
         }
 
         @Override
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 85a92d1..6212a77 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -59,6 +59,7 @@
     private final List<String> mConferenceableCallIds;
     private final Bundle mIntentExtras;
     private final Bundle mExtras;
+    private final long mCreationTimeMillis;
 
     public ParcelableCall(
             String id,
@@ -85,7 +86,8 @@
             int videoState,
             List<String> conferenceableCallIds,
             Bundle intentExtras,
-            Bundle extras) {
+            Bundle extras,
+            long creationTimeMillis) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -111,6 +113,7 @@
         mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
         mIntentExtras = intentExtras;
         mExtras = extras;
+        mCreationTimeMillis = creationTimeMillis;
     }
 
     /** The unique ID of the call. */
@@ -289,6 +292,13 @@
         return mIsVideoCallProviderChanged;
     }
 
+    /**
+     * @return The time the call was created, in milliseconds since the epoch.
+     */
+    public long getCreationTimeMillis() {
+        return mCreationTimeMillis;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
@@ -324,6 +334,7 @@
             int supportedAudioRoutes = source.readInt();
             boolean isRttCallChanged = source.readByte() == 1;
             ParcelableRttCall rttCall = source.readParcelable(classLoader);
+            long creationTimeMillis = source.readLong();
             return new ParcelableCall(
                     id,
                     state,
@@ -349,7 +360,8 @@
                     videoState,
                     conferenceableCallIds,
                     intentExtras,
-                    extras);
+                    extras,
+                    creationTimeMillis);
         }
 
         @Override
@@ -393,6 +405,7 @@
         destination.writeInt(mSupportedAudioRoutes);
         destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0));
         destination.writeParcelable(mRttCall, 0);
+        destination.writeLong(mCreationTimeMillis);
     }
 
     @Override
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 36ebc90..71067ae 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := 24
+LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 21
 
 # omit gradle 'build' dir
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 8461905..90f713b 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -155,7 +155,10 @@
     xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
     std::vector<UntranslatableSection>* out_untranslatable_sections) {
   // Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply.
-  std::vector<Span> span_stack;
+  // The stack elements refer to the indices in out_style_string->spans.
+  // By first adding to the out_style_string->spans vector, and then using the stack to refer
+  // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>.
+  std::vector<size_t> span_stack;
 
   // Clear the output variables.
   out_raw_string->clear();
@@ -192,7 +195,9 @@
           return false;
         }
 
-        span_stack.push_back(Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
+        out_style_string->spans.push_back(
+            Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
+        span_stack.push_back(out_style_string->spans.size() - 1);
       } else if (parser->element_namespace() == sXliffNamespaceUri) {
         if (parser->element_name() == "g") {
           if (untranslatable_start_depth) {
@@ -233,9 +238,8 @@
       if (parser->element_namespace().empty()) {
         // This is an HTML tag which we encode as a span. Update the span
         // stack and pop the top entry.
-        Span& top_span = span_stack.back();
+        Span& top_span = out_style_string->spans[span_stack.back()];
         top_span.last_char = builder.Utf16Len() - 1;
-        out_style_string->spans.push_back(std::move(top_span));
         span_stack.pop_back();
       } else if (untranslatable_start_depth == make_value(depth)) {
         // This is the end of an untranslatable section. Use UTF8 indices/lengths.
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index eefa320..8062c2e6 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -101,20 +101,24 @@
   // Use a surrogate pair unicode point so that we can verify that the span
   // indices use UTF-16 length and not UTF-8 length.
   std::string input =
-      "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
+      "<string name=\"foo\">This is my aunt\u2019s <b>fickle <small>string</small></b></string>";
   ASSERT_TRUE(TestParse(input));
 
   StyledString* str = test::GetValue<StyledString>(&table_, "string/foo");
   ASSERT_NE(nullptr, str);
 
-  const std::string expected_str = "This is my aunt\u2019s string";
+  const std::string expected_str = "This is my aunt\u2019s fickle string";
   EXPECT_EQ(expected_str, *str->value->str);
-  EXPECT_EQ(1u, str->value->spans.size());
+  EXPECT_EQ(2u, str->value->spans.size());
   EXPECT_TRUE(str->untranslatable_sections.empty());
 
   EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
   EXPECT_EQ(17u, str->value->spans[0].first_char);
-  EXPECT_EQ(23u, str->value->spans[0].last_char);
+  EXPECT_EQ(30u, str->value->spans[0].last_char);
+
+  EXPECT_EQ(std::string("small"), *str->value->spans[1].name);
+  EXPECT_EQ(24u, str->value->spans[1].first_char);
+  EXPECT_EQ(30u, str->value->spans[1].last_char);
 }
 
 TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index fad9edd..a031ea4 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -22,136 +22,194 @@
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
 #include "compile/Pseudolocalizer.h"
+#include "util/Util.h"
 
 using android::StringPiece;
+using android::StringPiece16;
 
 namespace aapt {
 
-std::unique_ptr<StyledString> PseudolocalizeStyledString(
-    StyledString* string, Pseudolocalizer::Method method, StringPool* pool) {
+// The struct that represents both Span objects and UntranslatableSections.
+struct UnifiedSpan {
+  // Only present for Span objects. If not present, this was an UntranslatableSection.
+  Maybe<std::string> tag;
+
+  // The UTF-16 index into the string where this span starts.
+  uint32_t first_char;
+
+  // The UTF-16 index into the string where this span ends, inclusive.
+  uint32_t last_char;
+};
+
+inline static bool operator<(const UnifiedSpan& left, const UnifiedSpan& right) {
+  if (left.first_char < right.first_char) {
+    return true;
+  } else if (left.first_char > right.first_char) {
+    return false;
+  } else if (left.last_char < right.last_char) {
+    return true;
+  }
+  return false;
+}
+
+inline static UnifiedSpan SpanToUnifiedSpan(const StringPool::Span& span) {
+  return UnifiedSpan{*span.name, span.first_char, span.last_char};
+}
+
+inline static UnifiedSpan UntranslatableSectionToUnifiedSpan(const UntranslatableSection& section) {
+  return UnifiedSpan{
+      {}, static_cast<uint32_t>(section.start), static_cast<uint32_t>(section.end) - 1};
+}
+
+// Merges the Span and UntranslatableSections of this StyledString into a single vector of
+// UnifiedSpans. This will first check that the Spans are sorted in ascending order.
+static std::vector<UnifiedSpan> MergeSpans(const StyledString& string) {
+  // Ensure the Spans are sorted and converted.
+  std::vector<UnifiedSpan> sorted_spans;
+  sorted_spans.reserve(string.value->spans.size());
+  std::transform(string.value->spans.begin(), string.value->spans.end(),
+                 std::back_inserter(sorted_spans), SpanToUnifiedSpan);
+
+  // Stable sort to ensure tag sequences like "<b><i>" are preserved.
+  std::stable_sort(sorted_spans.begin(), sorted_spans.end());
+
+  // Ensure the UntranslatableSections are sorted and converted.
+  std::vector<UnifiedSpan> sorted_untranslatable_sections;
+  sorted_untranslatable_sections.reserve(string.untranslatable_sections.size());
+  std::transform(string.untranslatable_sections.begin(), string.untranslatable_sections.end(),
+                 std::back_inserter(sorted_untranslatable_sections),
+                 UntranslatableSectionToUnifiedSpan);
+  std::sort(sorted_untranslatable_sections.begin(), sorted_untranslatable_sections.end());
+
+  std::vector<UnifiedSpan> merged_spans;
+  merged_spans.reserve(sorted_spans.size() + sorted_untranslatable_sections.size());
+  auto span_iter = sorted_spans.begin();
+  auto untranslatable_iter = sorted_untranslatable_sections.begin();
+  while (span_iter != sorted_spans.end() &&
+         untranslatable_iter != sorted_untranslatable_sections.end()) {
+    if (*span_iter < *untranslatable_iter) {
+      merged_spans.push_back(std::move(*span_iter));
+      ++span_iter;
+    } else {
+      merged_spans.push_back(std::move(*untranslatable_iter));
+      ++untranslatable_iter;
+    }
+  }
+
+  while (span_iter != sorted_spans.end()) {
+    merged_spans.push_back(std::move(*span_iter));
+    ++span_iter;
+  }
+
+  while (untranslatable_iter != sorted_untranslatable_sections.end()) {
+    merged_spans.push_back(std::move(*untranslatable_iter));
+    ++untranslatable_iter;
+  }
+  return merged_spans;
+}
+
+std::unique_ptr<StyledString> PseudolocalizeStyledString(StyledString* string,
+                                                         Pseudolocalizer::Method method,
+                                                         StringPool* pool) {
   Pseudolocalizer localizer(method);
 
-  const StringPiece original_text = *string->value->str;
+  // Collect the spans and untranslatable sections into one set of spans, sorted by first_char.
+  // This will effectively subdivide the string into multiple sections that can be individually
+  // pseudolocalized, while keeping the span indices synchronized.
+  std::vector<UnifiedSpan> merged_spans = MergeSpans(*string);
+
+  // All Span indices are UTF-16 based, according to the resources.arsc format expected by the
+  // runtime. So we will do all our processing in UTF-16, then convert back.
+  const std::u16string text16 = util::Utf8ToUtf16(*string->value->str);
+
+  // Convenient wrapper around the text that allows us to work with StringPieces.
+  const StringPiece16 text(text16);
+
+  // The new string.
+  std::string new_string = localizer.Start();
+
+  // The stack that keeps track of what nested Span we're in.
+  std::vector<size_t> span_stack;
+
+  // The current position in the original text.
+  uint32_t cursor = 0u;
+
+  // The current position in the new text.
+  uint32_t new_cursor = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_string.data()),
+                                             new_string.size(), false);
+
+  // We assume no nesting of untranslatable sections, since XLIFF doesn't allow it.
+  bool translatable = true;
+  size_t span_idx = 0u;
+  while (span_idx < merged_spans.size() || !span_stack.empty()) {
+    UnifiedSpan* span = span_idx >= merged_spans.size() ? nullptr : &merged_spans[span_idx];
+    UnifiedSpan* parent_span = span_stack.empty() ? nullptr : &merged_spans[span_stack.back()];
+
+    if (span != nullptr) {
+      if (parent_span == nullptr || parent_span->last_char > span->first_char) {
+        // There is no parent, or this span is the child of the parent.
+        // Pseudolocalize all the text until this span.
+        const StringPiece16 substr = text.substr(cursor, span->first_char - cursor);
+        cursor += substr.size();
+
+        // Pseudolocalize the substring.
+        std::string new_substr = util::Utf16ToUtf8(substr);
+        if (translatable) {
+          new_substr = localizer.Text(new_substr);
+        }
+        new_cursor += utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_substr.data()),
+                                           new_substr.size(), false);
+        new_string += new_substr;
+
+        // Rewrite the first_char.
+        span->first_char = new_cursor;
+        if (!span->tag) {
+          // An untranslatable section has begun!
+          translatable = false;
+        }
+        span_stack.push_back(span_idx);
+        ++span_idx;
+        continue;
+      }
+    }
+
+    if (parent_span != nullptr) {
+      // There is a parent, and either this span is not a child of it, or there are no more spans.
+      // Pop this off the stack.
+      const StringPiece16 substr = text.substr(cursor, parent_span->last_char - cursor + 1);
+      cursor += substr.size();
+
+      // Pseudolocalize the substring.
+      std::string new_substr = util::Utf16ToUtf8(substr);
+      if (translatable) {
+        new_substr = localizer.Text(new_substr);
+      }
+      new_cursor += utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(new_substr.data()),
+                                         new_substr.size(), false);
+      new_string += new_substr;
+
+      parent_span->last_char = new_cursor - 1;
+      if (parent_span->tag) {
+        // An end to an untranslatable section.
+        translatable = true;
+      }
+      span_stack.pop_back();
+    }
+  }
+
+  // Finish the pseudolocalization at the end of the string.
+  new_string += localizer.Text(util::Utf16ToUtf8(text.substr(cursor, text.size() - cursor)));
+  new_string += localizer.End();
 
   StyleString localized;
+  localized.str = std::move(new_string);
 
-  // Copy the spans. We will update their offsets when we localize.
-  localized.spans.reserve(string->value->spans.size());
-  for (const StringPool::Span& span : string->value->spans) {
-    localized.spans.push_back(
-        Span{*span.name, span.first_char, span.last_char});
-  }
-
-  // The ranges are all represented with a single value. This is the start of
-  // one range and end of another.
-  struct Range {
-    size_t start;
-
-    // If set to true, toggles the state of translatability.
-    bool toggle_translatability;
-
-    // Once the new string is localized, these are the pointers to the spans to adjust.
-    // Since this struct represents the start of one range and end of another,
-    // we have the two pointers respectively.
-    uint32_t* update_start;
-    uint32_t* update_end;
-  };
-
-  auto cmp = [](const Range& r, size_t index) -> bool {
-    return r.start < index;
-  };
-
-  // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
-  // The ranges are the spaces in between. In this example, with a total string
-  // length of 9, the vector represents: (0,1], (2,4], (5,6], (7,9]
-  //
-  std::vector<Range> ranges;
-  ranges.push_back(Range{0, false, nullptr, nullptr});
-  ranges.push_back(Range{original_text.size() - 1, false, nullptr, nullptr});
-  for (size_t i = 0; i < string->value->spans.size(); i++) {
-    const StringPool::Span& span = string->value->spans[i];
-
-    // Insert or update the Range marker for the start of this span.
-    auto iter =
-        std::lower_bound(ranges.begin(), ranges.end(), span.first_char, cmp);
-    if (iter != ranges.end() && iter->start == span.first_char) {
-      iter->update_start = &localized.spans[i].first_char;
-    } else {
-      ranges.insert(iter, Range{span.first_char, false, &localized.spans[i].first_char, nullptr});
-    }
-
-    // Insert or update the Range marker for the end of this span.
-    iter = std::lower_bound(ranges.begin(), ranges.end(), span.last_char, cmp);
-    if (iter != ranges.end() && iter->start == span.last_char) {
-      iter->update_end = &localized.spans[i].last_char;
-    } else {
-      ranges.insert(iter, Range{span.last_char, false, nullptr, &localized.spans[i].last_char});
+  // Convert the UnifiedSpans into regular Spans, skipping the UntranslatableSections.
+  for (UnifiedSpan& span : merged_spans) {
+    if (span.tag) {
+      localized.spans.push_back(Span{std::move(span.tag.value()), span.first_char, span.last_char});
     }
   }
-
-  // Parts of the string may be untranslatable. Merge those ranges
-  // in as well, so that we have continuous sections of text to
-  // feed into the pseudolocalizer.
-  // We do this by marking the beginning of a range as either toggling
-  // the translatability state or not.
-  for (const UntranslatableSection& section : string->untranslatable_sections) {
-    auto iter = std::lower_bound(ranges.begin(), ranges.end(), section.start, cmp);
-    if (iter != ranges.end() && iter->start == section.start) {
-      // An existing span starts (or ends) here. We just need to mark that
-      // the translatability should toggle here. If translatability was
-      // already being toggled, then that means we have two adjacent ranges of untranslatable
-      // text, so remove the toggle and only toggle at the end of this range,
-      // effectively merging these ranges.
-      iter->toggle_translatability = !iter->toggle_translatability;
-    } else {
-      // Insert a new range that specifies to toggle the translatability.
-      iter = ranges.insert(iter, Range{section.start, true, nullptr, nullptr});
-    }
-
-    // Update/create an end to the untranslatable section.
-    iter = std::lower_bound(iter, ranges.end(), section.end, cmp);
-    if (iter != ranges.end() && iter->start == section.end) {
-      iter->toggle_translatability = true;
-    } else {
-      iter = ranges.insert(iter, Range{section.end, true, nullptr, nullptr});
-    }
-  }
-
-  localized.str += localizer.Start();
-
-  // Iterate over the ranges and localize each section.
-  // The text starts as translatable, and each time a range has toggle_translatability
-  // set to true, we toggle whether to translate or not.
-  // This assumes no untranslatable ranges overlap.
-  bool translatable = true;
-  for (size_t i = 0; i < ranges.size(); i++) {
-    const size_t start = ranges[i].start;
-    size_t len = original_text.size() - start;
-    if (i + 1 < ranges.size()) {
-      len = ranges[i + 1].start - start;
-    }
-
-    if (ranges[i].update_start) {
-      *ranges[i].update_start = localized.str.size();
-    }
-
-    if (ranges[i].update_end) {
-      *ranges[i].update_end = localized.str.size();
-    }
-
-    if (ranges[i].toggle_translatability) {
-      translatable = !translatable;
-    }
-
-    if (translatable) {
-      localized.str += localizer.Text(original_text.substr(start, len));
-    } else {
-      localized.str += original_text.substr(start, len);
-    }
-  }
-
-  localized.str += localizer.End();
-
   return util::make_unique<StyledString>(pool->MakeRef(localized));
 }
 
@@ -175,8 +233,7 @@
         if (sub_visitor.value) {
           localized->values[i] = std::move(sub_visitor.item);
         } else {
-          localized->values[i] =
-              std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
+          localized->values[i] = std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
         }
       }
     }
@@ -210,8 +267,7 @@
     }
     result += localizer_.End();
 
-    std::unique_ptr<String> localized =
-        util::make_unique<String>(pool_->MakeRef(result));
+    std::unique_ptr<String> localized = util::make_unique<String>(pool_->MakeRef(result));
     localized->SetSource(string->GetSource());
     localized->SetWeak(true);
     item = std::move(localized);
@@ -282,14 +338,10 @@
   }
 }
 
-/**
- * A value is pseudolocalizable if it does not define a locale (or is the
- * default locale)
- * and is translatable.
- */
+// A value is pseudolocalizable if it does not define a locale (or is the default locale) and is
+// translatable.
 static bool IsPseudolocalizable(ResourceConfigValue* config_value) {
-  const int diff =
-      config_value->config.diff(ConfigDescription::DefaultConfig());
+  const int diff = config_value->config.diff(ConfigDescription::DefaultConfig());
   if (diff & ConfigDescription::CONFIG_LOCALE) {
     return false;
   }
@@ -298,19 +350,16 @@
 
 }  // namespace
 
-bool PseudolocaleGenerator::Consume(IAaptContext* context,
-                                    ResourceTable* table) {
+bool PseudolocaleGenerator::Consume(IAaptContext* context, ResourceTable* table) {
   for (auto& package : table->packages) {
     for (auto& type : package->types) {
       for (auto& entry : type->entries) {
-        std::vector<ResourceConfigValue*> values =
-            entry->FindValuesIf(IsPseudolocalizable);
-
+        std::vector<ResourceConfigValue*> values = entry->FindValuesIf(IsPseudolocalizable);
         for (ResourceConfigValue* value : values) {
-          PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
-                                 &table->string_pool, entry.get());
-          PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
-                                 &table->string_pool, entry.get());
+          PseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, &table->string_pool,
+                                 entry.get());
+          PseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value, &table->string_pool,
+                                 entry.get());
         }
       }
     }
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index 4db37db..b08e1da 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -25,7 +25,7 @@
   StringPool pool;
   StyleString original_style;
   original_style.str = "Hello world!";
-  original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
+  original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}};
 
   std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
       util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
@@ -34,22 +34,19 @@
   EXPECT_EQ(original_style.str, *new_string->value->str);
   ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
 
-  EXPECT_EQ(std::string("He").size(), new_string->value->spans[0].first_char);
-  EXPECT_EQ(std::string("Hel").size(), new_string->value->spans[0].last_char);
-  EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+  EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name);
+  EXPECT_EQ(std::u16string(u"H").size(), new_string->value->spans[0].first_char);
+  EXPECT_EQ(std::u16string(u"Hello worl").size(), new_string->value->spans[0].last_char);
 
-  EXPECT_EQ(std::string("Hello ").size(),
-            new_string->value->spans[1].first_char);
-  EXPECT_EQ(std::string("Hello w").size(),
-            new_string->value->spans[1].last_char);
   EXPECT_EQ(std::string("b"), *new_string->value->spans[1].name);
+  EXPECT_EQ(std::u16string(u"He").size(), new_string->value->spans[1].first_char);
+  EXPECT_EQ(std::u16string(u"Hel").size(), new_string->value->spans[1].last_char);
 
-  EXPECT_EQ(std::string("H").size(), new_string->value->spans[2].first_char);
-  EXPECT_EQ(std::string("Hello worl").size(),
-            new_string->value->spans[2].last_char);
-  EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
+  EXPECT_EQ(std::string("b"), *new_string->value->spans[2].name);
+  EXPECT_EQ(std::u16string(u"Hello ").size(), new_string->value->spans[2].first_char);
+  EXPECT_EQ(std::u16string(u"Hello w").size(), new_string->value->spans[2].last_char);
 
-  original_style.spans.push_back(Span{"em", 0, 11u});
+  original_style.spans.insert(original_style.spans.begin(), Span{"em", 0, 11u});
 
   new_string = PseudolocalizeStyledString(
       util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
@@ -58,23 +55,128 @@
   EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str);
   ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size());
 
-  EXPECT_EQ(std::string("[Ĥé").size(), new_string->value->spans[0].first_char);
-  EXPECT_EQ(std::string("[Ĥéļ").size(), new_string->value->spans[0].last_char);
+  EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
+  EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļð").size(), new_string->value->spans[0].last_char);
 
-  EXPECT_EQ(std::string("[Ĥéļļö ").size(),
+  EXPECT_EQ(std::u16string(u"[Ĥ").size(), new_string->value->spans[1].first_char);
+  EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļ").size(), new_string->value->spans[1].last_char);
+
+  EXPECT_EQ(std::u16string(u"[Ĥé").size(), new_string->value->spans[2].first_char);
+  EXPECT_EQ(std::u16string(u"[Ĥéļ").size(), new_string->value->spans[2].last_char);
+
+  EXPECT_EQ(std::u16string(u"[Ĥéļļö ").size(), new_string->value->spans[3].first_char);
+  EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵ").size(), new_string->value->spans[3].last_char);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentNestedTags) {
+  StringPool pool;
+  StyleString original_style;
+  original_style.str = "bold";
+  original_style.spans = {Span{"b", 0, 3}, Span{"i", 0, 3}};
+
+  std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+      util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+      Pseudolocalizer::Method::kAccent, &pool);
+  ASSERT_NE(nullptr, new_string);
+  ASSERT_EQ(2u, new_string->value->spans.size());
+  EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+
+  EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+  EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
+  EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[0].last_char);
+
+  EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
+  EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[1].first_char);
+  EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentTagsUnsorted) {
+  StringPool pool;
+  StyleString original_style;
+  original_style.str = "bold";
+  original_style.spans = {Span{"i", 2, 3}, Span{"b", 0, 1}};
+
+  std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+      util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+      Pseudolocalizer::Method::kAccent, &pool);
+  ASSERT_NE(nullptr, new_string);
+  ASSERT_EQ(2u, new_string->value->spans.size());
+  EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str);
+
+  EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+  EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char);
+  EXPECT_EQ(std::u16string(u"[ɓ").size(), new_string->value->spans[0].last_char);
+
+  EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
+  EXPECT_EQ(std::u16string(u"[ɓö").size(), new_string->value->spans[1].first_char);
+  EXPECT_EQ(std::u16string(u"[ɓöļ").size(), new_string->value->spans[1].last_char);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeNestedAndAdjacentTags) {
+  StringPool pool;
+  StyleString original_style;
+  original_style.str = "This sentence is not what you think it is at all.";
+  original_style.spans = {Span{"b", 16u, 19u}, Span{"em", 29u, 47u}, Span{"i", 38u, 40u},
+                          Span{"b", 44u, 47u}};
+
+  std::unique_ptr<StyledString> new_string = PseudolocalizeStyledString(
+      util::make_unique<StyledString>(pool.MakeRef(original_style)).get(),
+      Pseudolocalizer::Method::kAccent, &pool);
+  ASSERT_NE(nullptr, new_string);
+  ASSERT_EQ(4u, new_string->value->spans.size());
+  EXPECT_EQ(std::string(
+                "[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"),
+            *new_string->value->str);
+
+  EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñö").size(), new_string->value->spans[0].last_char);
+
+  EXPECT_EQ(std::string("em"), *new_string->value->spans[1].name);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû").size(),
             new_string->value->spans[1].first_char);
-  EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(),
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(),
             new_string->value->spans[1].last_char);
 
-  EXPECT_EQ(std::string("[Ĥ").size(), new_string->value->spans[2].first_char);
-  EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(),
+  EXPECT_EQ(std::string("i"), *new_string->value->spans[2].name);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ").size(),
+            new_string->value->spans[2].first_char);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ î").size(),
             new_string->value->spans[2].last_char);
 
-  EXPECT_EQ(std::string("[").size(), new_string->value->spans[3].first_char);
-  EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(),
+  EXPECT_EQ(std::string("b"), *new_string->value->spans[3].name);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ").size(),
+            new_string->value->spans[3].first_char);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļ").size(),
             new_string->value->spans[3].last_char);
 }
 
+TEST(PseudolocaleGeneratorTest, PseudolocalizePartsOfString) {
+  StringPool pool;
+  StyleString original_style;
+  original_style.str = "This should NOT be pseudolocalized.";
+  original_style.spans = {Span{"em", 4u, 14u}, Span{"i", 18u, 33u}};
+  std::unique_ptr<StyledString> original_string =
+      util::make_unique<StyledString>(pool.MakeRef(original_style));
+  original_string->untranslatable_sections = {UntranslatableSection{11u, 15u}};
+
+  std::unique_ptr<StyledString> new_string =
+      PseudolocalizeStyledString(original_string.get(), Pseudolocalizer::Method::kAccent, &pool);
+  ASSERT_NE(nullptr, new_string);
+  ASSERT_EQ(2u, new_string->value->spans.size());
+  EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"),
+            *new_string->value->str);
+
+  EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NO").size(), new_string->value->spans[0].last_char);
+
+  EXPECT_EQ(std::string("i"), *new_string->value->spans[1].name);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé").size(), new_string->value->spans[1].first_char);
+  EXPECT_EQ(std::u16string(u"[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžé").size(),
+            new_string->value->spans[1].last_char);
+}
+
 TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) {
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
@@ -138,7 +240,7 @@
   {
     StyleString original_style;
     original_style.str = "Hello world!";
-    original_style.spans = {Span{"b", 2, 3}, Span{"b", 6, 7}, Span{"i", 1, 10}};
+    original_style.spans = {Span{"i", 1, 10}, Span{"b", 2, 3}, Span{"b", 6, 7}};
 
     auto styled_string =
         util::make_unique<StyledString>(table->string_pool.MakeRef(original_style));