Merge "Add lower case charging string." into oc-dev
diff --git a/Android.mk b/Android.mk
index ee7d471..915f103 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,6 +321,8 @@
 	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\
@@ -728,6 +731,7 @@
 	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 15d308e..17fe1ae 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
@@ -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();
@@ -5116,6 +5117,7 @@
     method public java.lang.String getChannel();
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
+    method public java.lang.CharSequence getSettingsText();
     method public java.lang.String getShortcutId();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
@@ -5160,6 +5162,8 @@
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
     field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
     field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
     field public static final java.lang.String EXTRA_PEOPLE = "android.people";
     field public static final java.lang.String EXTRA_PICTURE = "android.picture";
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
@@ -5345,6 +5349,7 @@
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
     method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence);
     method public android.app.Notification.Builder setShortcutId(java.lang.String);
     method public android.app.Notification.Builder setShowWhen(boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
@@ -10802,6 +10807,7 @@
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+    field public static final int PROTECTION_FLAG_RUNTIME_ONLY = 8192; // 0x2000
     field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
@@ -12850,7 +12856,7 @@
 
   public static class ColorSpace.Connector {
     method public android.graphics.ColorSpace getDestination();
-    method public android.graphics.ColorSpace.RenderIntent getIntent();
+    method public android.graphics.ColorSpace.RenderIntent getRenderIntent();
     method public android.graphics.ColorSpace getSource();
     method public float[] transform(float, float, float);
     method public float[] transform(float[]);
@@ -25557,7 +25563,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
   }
 
@@ -25591,7 +25597,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);
   }
 
@@ -37016,7 +37021,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
@@ -37068,8 +37073,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 {
diff --git a/api/system-current.txt b/api/system-current.txt
index f74bc3a..8a9d8aa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -53,6 +53,7 @@
     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";
@@ -1727,6 +1728,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
@@ -4931,7 +4933,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();
@@ -5289,6 +5291,7 @@
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
     method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
+    method public java.lang.CharSequence getSettingsText();
     method public java.lang.String getShortcutId();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
@@ -5334,6 +5337,8 @@
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
     field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
     field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
     field public static final java.lang.String EXTRA_PEOPLE = "android.people";
     field public static final java.lang.String EXTRA_PICTURE = "android.picture";
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
@@ -5521,6 +5526,7 @@
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
     method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence);
     method public android.app.Notification.Builder setShortcutId(java.lang.String);
     method public android.app.Notification.Builder setShowWhen(boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
@@ -11550,6 +11556,7 @@
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+    field public static final int PROTECTION_FLAG_RUNTIME_ONLY = 8192; // 0x2000
     field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
@@ -13612,7 +13619,7 @@
 
   public static class ColorSpace.Connector {
     method public android.graphics.ColorSpace getDestination();
-    method public android.graphics.ColorSpace.RenderIntent getIntent();
+    method public android.graphics.ColorSpace.RenderIntent getRenderIntent();
     method public android.graphics.ColorSpace getSource();
     method public float[] transform(float, float, float);
     method public float[] transform(float[]);
@@ -27718,13 +27725,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 +27760,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);
   }
@@ -33585,6 +33588,14 @@
     method public void open();
   }
 
+  public final class ConfigUpdate {
+    field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+    field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
+    field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
+    field public static final java.lang.String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
+    field public static final java.lang.String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+  }
+
   public abstract class CountDownTimer {
     ctor public CountDownTimer(long, long);
     method public final synchronized void cancel();
@@ -40081,7 +40092,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
@@ -40133,8 +40144,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 {
@@ -40360,7 +40373,6 @@
     method public int getUser();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
-    field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
     field public static final java.lang.String KEY_PEOPLE = "key_people";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
   }
@@ -40415,14 +40427,10 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
-    method public void createNotificationChannel(java.lang.String, android.app.NotificationChannel);
-    method public void deleteNotificationChannel(java.lang.String, java.lang.String);
-    method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public final void unsnoozeNotification(java.lang.String);
-    method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
@@ -40620,6 +40628,36 @@
 
 }
 
+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 {
diff --git a/api/test-current.txt b/api/test-current.txt
index b51739b..5f718b9 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
@@ -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();
@@ -5128,6 +5129,7 @@
     method public java.lang.String getChannel();
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
+    method public java.lang.CharSequence getSettingsText();
     method public java.lang.String getShortcutId();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
@@ -5172,6 +5174,8 @@
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
     field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
     field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
+    field public static final java.lang.String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final java.lang.String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
     field public static final java.lang.String EXTRA_PEOPLE = "android.people";
     field public static final java.lang.String EXTRA_PICTURE = "android.picture";
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
@@ -5357,6 +5361,7 @@
     method public android.app.Notification.Builder setProgress(int, int, boolean);
     method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
     method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+    method public android.app.Notification.Builder setSettingsText(java.lang.CharSequence);
     method public android.app.Notification.Builder setShortcutId(java.lang.String);
     method public android.app.Notification.Builder setShowWhen(boolean);
     method public android.app.Notification.Builder setSmallIcon(int);
@@ -10842,6 +10847,7 @@
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+    field public static final int PROTECTION_FLAG_RUNTIME_ONLY = 8192; // 0x2000
     field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
@@ -12900,7 +12906,7 @@
 
   public static class ColorSpace.Connector {
     method public android.graphics.ColorSpace getDestination();
-    method public android.graphics.ColorSpace.RenderIntent getIntent();
+    method public android.graphics.ColorSpace.RenderIntent getRenderIntent();
     method public android.graphics.ColorSpace getSource();
     method public float[] transform(float, float, float);
     method public float[] transform(float[]);
@@ -25670,7 +25676,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
   }
 
@@ -25704,7 +25710,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);
   }
 
@@ -37174,7 +37179,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
@@ -37226,8 +37231,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 {
@@ -37453,7 +37460,6 @@
     method public int getUser();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
-    field public static final java.lang.String KEY_CHANNEL_ID = "key_channel_id";
     field public static final java.lang.String KEY_PEOPLE = "key_people";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
   }
@@ -37508,14 +37514,10 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
-    method public void createNotificationChannel(java.lang.String, android.app.NotificationChannel);
-    method public void deleteNotificationChannel(java.lang.String, java.lang.String);
-    method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public final void unsnoozeNotification(java.lang.String);
-    method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
@@ -46038,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);
@@ -47309,6 +47312,7 @@
     field public static final deprecated int MEMORY_TYPE_HARDWARE = 1; // 0x1
     field public static final deprecated int MEMORY_TYPE_NORMAL = 0; // 0x0
     field public static final deprecated int MEMORY_TYPE_PUSH_BUFFERS = 3; // 0x3
+    field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
     field public static final int ROTATION_ANIMATION_CHANGED = 4096; // 0x1000
     field public static final int ROTATION_ANIMATION_CROSSFADE = 1; // 0x1
     field public static final int ROTATION_ANIMATION_JUMPCUT = 2; // 0x2
@@ -47369,6 +47373,7 @@
     field public java.lang.String packageName;
     field public int preferredDisplayModeId;
     field public deprecated float preferredRefreshRate;
+    field public int privateFlags;
     field public int rotationAnimation;
     field public float screenBrightness;
     field public int screenOrientation;
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index db17b28..ce114fd 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -101,8 +101,10 @@
                     doCompress = true;
                 } else if ("-nocompress".equals(arg)) {
                     doCompress = false;
-                } else if ("-includekeyvalue".equals(arg)) {
+                } else if ("-keyvalue".equals(arg)) {
                     doKeyValue = true;
+                } else if ("-nokeyvalue".equals(arg)) {
+                    doKeyValue = false;
                 } else {
                     Log.w(TAG, "Unknown backup flag " + arg);
                     continue;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c0505eb..af3bf2a 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);
     }
 
@@ -4166,25 +4177,14 @@
                 mTaskDescription.setPrimaryColor(colorPrimary);
             }
         }
-
-        int colorBackground = a.getColor(
-                com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0);
-        if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) {
-            mTaskDescription.setBackgroundColor(colorBackground);
+        // For dev-preview only.
+        if (mTaskDescription.getBackgroundColor() == 0) {
+            int colorBackground = a.getColor(
+                    com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0);
+            if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) {
+                mTaskDescription.setBackgroundColor(colorBackground);
+            }
         }
-
-        final int statusBarColor = a.getColor(
-                com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0);
-        if (statusBarColor != 0) {
-            mTaskDescription.setStatusBarColor(statusBarColor);
-        }
-
-        final int navigationBarColor = a.getColor(
-                com.android.internal.R.styleable.ActivityTaskDescription_navigationBarColor, 0);
-        if (navigationBarColor != 0) {
-            mTaskDescription.setNavigationBarColor(navigationBarColor);
-        }
-
         a.recycle();
         setTaskDescription(mTaskDescription);
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index aede1bb..4004bd6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1145,8 +1145,6 @@
         private String mIconFilename;
         private int mColorPrimary;
         private int mColorBackground;
-        private int mStatusBarColor;
-        private int mNavigationBarColor;
 
         /**
          * Creates the TaskDescription to the specified values.
@@ -1157,7 +1155,7 @@
          *                     opaque.
          */
         public TaskDescription(String label, Bitmap icon, int colorPrimary) {
-            this(label, icon, null, colorPrimary, 0, 0, 0);
+            this(label, icon, null, colorPrimary, 0);
             if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
                 throw new RuntimeException("A TaskDescription's primary color should be opaque");
             }
@@ -1170,7 +1168,7 @@
          * @param icon An icon that represents the current state of this activity.
          */
         public TaskDescription(String label, Bitmap icon) {
-            this(label, icon, null, 0, 0, 0, 0);
+            this(label, icon, null, 0, 0);
         }
 
         /**
@@ -1179,26 +1177,24 @@
          * @param label A label and description of the current state of this activity.
          */
         public TaskDescription(String label) {
-            this(label, null, null, 0, 0, 0, 0);
+            this(label, null, null, 0, 0);
         }
 
         /**
          * Creates an empty TaskDescription.
          */
         public TaskDescription() {
-            this(null, null, null, 0, 0, 0, 0);
+            this(null, null, null, 0, 0);
         }
 
         /** @hide */
         public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
-                int colorBackground, int statusBarColor, int navigationBarColor) {
+                int colorBackground) {
             mLabel = label;
             mIcon = icon;
             mIconFilename = iconFilename;
             mColorPrimary = colorPrimary;
             mColorBackground = colorBackground;
-            mStatusBarColor = statusBarColor;
-            mNavigationBarColor = navigationBarColor;
         }
 
         /**
@@ -1218,8 +1214,6 @@
             mIconFilename = other.mIconFilename;
             mColorPrimary = other.mColorPrimary;
             mColorBackground = other.mColorBackground;
-            mStatusBarColor = other.mStatusBarColor;
-            mNavigationBarColor = other.mNavigationBarColor;
         }
 
         private TaskDescription(Parcel source) {
@@ -1259,20 +1253,6 @@
         }
 
         /**
-         * @hide
-         */
-        public void setStatusBarColor(int statusBarColor) {
-            mStatusBarColor = statusBarColor;
-        }
-
-        /**
-         * @hide
-         */
-        public void setNavigationBarColor(int navigationBarColor) {
-            mNavigationBarColor = navigationBarColor;
-        }
-
-        /**
          * Sets the icon for this task description.
          * @hide
          */
@@ -1345,20 +1325,6 @@
             return mColorBackground;
         }
 
-        /**
-         * @hide
-         */
-        public int getStatusBarColor() {
-            return mStatusBarColor;
-        }
-
-        /**
-         * @hide
-         */
-        public int getNavigationBarColor() {
-            return mNavigationBarColor;
-        }
-
         /** @hide */
         public void saveToXml(XmlSerializer out) throws IOException {
             if (mLabel != null) {
@@ -1411,8 +1377,6 @@
             }
             dest.writeInt(mColorPrimary);
             dest.writeInt(mColorBackground);
-            dest.writeInt(mStatusBarColor);
-            dest.writeInt(mNavigationBarColor);
             if (mIconFilename == null) {
                 dest.writeInt(0);
             } else {
@@ -1426,8 +1390,6 @@
             mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
             mColorPrimary = source.readInt();
             mColorBackground = source.readInt();
-            mStatusBarColor = source.readInt();
-            mNavigationBarColor = source.readInt();
             mIconFilename = source.readInt() > 0 ? source.readString() : null;
         }
 
@@ -1445,9 +1407,7 @@
         public String toString() {
             return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
                     " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
-                    " colorBackground: " + mColorBackground +
-                    " statusBarColor: " + mColorBackground +
-                    " navigationBarColor: " + mNavigationBarColor;
+                    " colorBackground: " + mColorBackground;
         }
     }
 
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 3107453..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,6 +2930,7 @@
     }
 
     public void noteStateNotSaved() {
+        mSavedNonConfig = null;
         mStateSaved = false;
         final int addedCount = mAdded == null ? 0 : mAdded.size();
         for (int i = 0; i < addedCount; i++) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 43cad5b..61dacef 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -104,10 +104,6 @@
     void applyEnqueuedAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
     void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
     void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments);
-    void createNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel);
-    void updateNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel);
-    void deleteNotificationChannelFromAssistant(in INotificationListener token, String pkg, String channelId);
-    ParceledListSlice getNotificationChannelsFromAssistant(in INotificationListener token, String pkg);
     void unsnoozeNotificationFromAssistant(in INotificationListener token, String key);
 
     ComponentName getEffectsSuppressor();
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index 2bdfa99..88399e5 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.InstantAppResolveInfo;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -28,9 +29,12 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.os.SomeArgs;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -39,6 +43,9 @@
  */
 @SystemApi
 public abstract class InstantAppResolverService extends Service {
+    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+    private static final String TAG = "PackageManager";
+
     /** @hide */
     public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
     /** @hide */
@@ -132,11 +139,19 @@
     @Deprecated
     void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
             InstantAppResolutionCallback callback) {
+        if (DEBUG_EPHEMERAL) {
+            Slog.d(TAG, "Instant resolver; getInstantAppResolveInfo;"
+                    + " prefix: " + Arrays.toString(digestPrefix));
+        }
         onGetInstantAppResolveInfo(digestPrefix, token, callback);
     }
     @Deprecated
     void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
             InstantAppResolutionCallback callback) {
+        if (DEBUG_EPHEMERAL) {
+            Slog.d(TAG, "Instant resolver; getInstantAppIntentFilter;"
+                    + " prefix: " + Arrays.toString(digestPrefix));
+        }
         onGetInstantAppIntentFilter(digestPrefix, token, callback);
     }
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 161dd25..6d7486b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -103,8 +103,7 @@
 
     /**
      * An activity that provides a user interface for adjusting notification preferences for its
-     * containing application. Optional but recommended for apps that post
-     * {@link android.app.Notification Notifications}.
+     * containing application.
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
@@ -113,11 +112,25 @@
     /**
      * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
      * contain a {@link NotificationChannel#getId() channel id} that can be used to narrow down
-     * what in app notifications settings should be shown.
+     * what settings should be shown in the target app.
      */
     public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
 
     /**
+     * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
+     * contain the tag provided to {@link NotificationManager#notify(String, int, Notification)}
+     * that can be used to narrow down what settings should be shown in the target app.
+     */
+    public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+
+    /**
+     * Optional extra for {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}. If provided, will
+     * contain the id provided to {@link NotificationManager#notify(String, int, Notification)}
+     * that can be used to narrow down what settings should be shown in the target app.
+     */
+    public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+
+    /**
      * Use all default values (where applicable).
      */
     public static final int DEFAULT_ALL = ~0;
@@ -1082,6 +1095,7 @@
     private long mTimeout;
 
     private String mShortcutId;
+    private CharSequence mSettingsText;
 
     /**
      * If this notification is being shown as a badge, always show as a number.
@@ -1851,6 +1865,10 @@
         }
 
         mBadgeIcon = parcel.readInt();
+
+        if (parcel.readInt() != 0) {
+            mSettingsText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        }
     }
 
     @Override
@@ -1960,6 +1978,9 @@
 
         that.mChannelId = this.mChannelId;
         that.mTimeout = this.mTimeout;
+        that.mShortcutId = this.mShortcutId;
+        that.mBadgeIcon = this.mBadgeIcon;
+        that.mSettingsText = this.mSettingsText;
 
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
@@ -2229,6 +2250,13 @@
         }
 
         parcel.writeInt(mBadgeIcon);
+
+        if (mSettingsText != null) {
+            parcel.writeInt(1);
+            TextUtils.writeToParcel(mSettingsText, parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     /**
@@ -2458,6 +2486,14 @@
         return mShortcutId;
     }
 
+
+    /**
+     * Returns the settings text provided to {@link Builder#setSettingsText(CharSequence)}.
+     */
+    public CharSequence getSettingsText() {
+        return mSettingsText;
+    }
+
     /**
      * The small icon representing this notification in the status bar and content view.
      *
@@ -2887,6 +2923,24 @@
         }
 
         /**
+         * Provides text that will appear as a link to your application's settings.
+         *
+         * <p>This text does not appear within notification {@link Style templates} but may
+         * appear when the user uses an affordance to learn more about the notification.
+         * Additionally, this text will not appear unless you provide a valid link target by
+         * handling {@link #INTENT_CATEGORY_NOTIFICATION_PREFERENCES}.
+         *
+         * <p>This text is meant to be concise description about what the user can customize
+         * when they click on this link. The recommended maximum length is 40 characters.
+         * @param text
+         * @return
+         */
+        public Builder setSettingsText(CharSequence text) {
+            mN.mSettingsText = safeCharSequence(text);
+            return this;
+        }
+
+        /**
          * Set the remote input history.
          *
          * This should be set to the most recent inputs that have been sent
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/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index fe51633..545aef5 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -599,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;
@@ -756,6 +760,7 @@
             boolean writeSensitive = true;
 
             int flags = mFlags & ~FLAGS_ALL_CONTROL;
+
             if (mId != View.NO_ID) {
                 flags |= FLAGS_HAS_ID;
             }
@@ -810,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) {
@@ -829,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) {
@@ -959,6 +978,11 @@
             return mAutofillValue;
         }
 
+        /** @hide **/
+        public void setAutofillOverlay(AutofillOverlay overlay) {
+            mAutofillOverlay = overlay;
+        }
+
         /**
          * Gets the options that can be used to autofill this structure.
          *
@@ -1340,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/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9457d15..940447c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3114,6 +3114,7 @@
 
         if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
             if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0
+                    && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
                     PermissionInfo.PROTECTION_SIGNATURE) {
                 outError[0] = "<permission>  protectionLevel specifies a non-ephemeral flag but is "
@@ -6172,6 +6173,7 @@
             cpuAbiOverride = dest.readString();
             use32bitAbi = (dest.readInt() == 1);
             restrictUpdateHash = dest.createByteArray();
+            visibleToInstantApps = dest.readInt() == 1;
         }
 
         private static void internStringArrayList(List<String> list) {
@@ -6286,6 +6288,7 @@
             dest.writeString(cpuAbiOverride);
             dest.writeInt(use32bitAbi ? 1 : 0);
             dest.writeByteArray(restrictUpdateHash);
+            dest.writeInt(visibleToInstantApps ? 1 : 0);
         }
 
 
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 0703138..694e607 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -131,6 +131,13 @@
     public static final int PROTECTION_FLAG_EPHEMERAL = 0x1000;
 
     /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>runtime</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_RUNTIME_ONLY = 0x2000;
+
+    /**
      * Mask for {@link #protectionLevel}: the basic protection type.
      */
     public static final int PROTECTION_MASK_BASE = 0xf;
@@ -250,6 +257,9 @@
         if ((level&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0) {
             protLevel += "|ephemeral";
         }
+        if ((level&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
+            protLevel += "|runtime";
+        }
         return protLevel;
     }
 
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/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2aa6af6..46ad3f0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -67,6 +67,15 @@
          *
          * @hide
          */
+        public Key(String name, Class<T> type, long vendorId) {
+            mKey = new CameraMetadataNative.Key<T>(name,  type, vendorId);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name,  type);
         }
@@ -99,6 +108,15 @@
         }
 
         /**
+         * Return vendor tag id.
+         *
+         * @hide
+         */
+        public long getVendorId() {
+            return mKey.getVendorId();
+        }
+
+        /**
          * {@inheritDoc}
          */
         @Override
@@ -159,6 +177,7 @@
      */
     public CameraCharacteristics(CameraMetadataNative properties) {
         mProperties = CameraMetadataNative.move(properties);
+        setNativeInstance(mProperties);
     }
 
     /**
@@ -227,7 +246,7 @@
         }
 
         mKeys = Collections.unmodifiableList(
-                getKeysStatic(getClass(), getKeyClass(), this, filterTags));
+                getKeys(getClass(), getKeyClass(), this, filterTags));
         return mKeys;
     }
 
@@ -320,7 +339,7 @@
                     "metadataClass must be a subclass of CameraMetadata");
         }
 
-        List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
+        List<TKey> staticKeyList = getKeys(
                 metadataClass, keyClass, /*instance*/null, filterTags);
         return Collections.unmodifiableList(staticKeyList);
     }
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e289627..8c8c49f 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -52,6 +52,7 @@
 
     private static final String TAG = "CameraMetadataAb";
     private static final boolean DEBUG = false;
+    private CameraMetadataNative mNativeInstance = null;
 
     /**
      * Set a camera metadata field to a value. The field definitions can be
@@ -89,6 +90,13 @@
      /**
       * @hide
       */
+     protected void setNativeInstance(CameraMetadataNative nativeInstance) {
+        mNativeInstance = nativeInstance;
+     }
+
+     /**
+      * @hide
+      */
      protected abstract Class<TKey> getKeyClass();
 
     /**
@@ -108,7 +116,7 @@
     public List<TKey> getKeys() {
         Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
         return Collections.unmodifiableList(
-                getKeysStatic(thisClass, getKeyClass(), this, /*filterTags*/null));
+                getKeys(thisClass, getKeyClass(), this, /*filterTags*/null));
     }
 
     /**
@@ -126,7 +134,7 @@
      * </p>
      */
      /*package*/ @SuppressWarnings("unchecked")
-    static <TKey> ArrayList<TKey> getKeysStatic(
+    <TKey> ArrayList<TKey> getKeys(
              Class<?> type, Class<TKey> keyClass,
              CameraMetadata<TKey> instance,
              int[] filterTags) {
@@ -173,23 +181,31 @@
             }
         }
 
-        ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass);
+        if (null == mNativeInstance) {
+            return keyList;
+        }
+
+        ArrayList<TKey> vendorKeys = mNativeInstance.getAllVendorKeys(keyClass);
 
         if (vendorKeys != null) {
             for (TKey k : vendorKeys) {
                 String keyName;
+                long vendorId;
                 if (k instanceof CaptureRequest.Key<?>) {
                     keyName = ((CaptureRequest.Key<?>) k).getName();
+                    vendorId = ((CaptureRequest.Key<?>) k).getVendorId();
                 } else if (k instanceof CaptureResult.Key<?>) {
                     keyName = ((CaptureResult.Key<?>) k).getName();
+                    vendorId = ((CaptureResult.Key<?>) k).getVendorId();
                 } else if (k instanceof CameraCharacteristics.Key<?>) {
                     keyName = ((CameraCharacteristics.Key<?>) k).getName();
+                    vendorId = ((CameraCharacteristics.Key<?>) k).getVendorId();
                 } else {
                     continue;
                 }
 
                 if (filterTags == null || Arrays.binarySearch(filterTags,
-                        CameraMetadataNative.getTag(keyName)) >= 0) {
+                        CameraMetadataNative.getTag(keyName, vendorId)) >= 0) {
                     keyList.add(k);
                 }
             }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 12b46c1..1cf8f03 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -101,6 +101,15 @@
          *
          * @hide
          */
+        public Key(String name, Class<T> type, long vendorId) {
+            mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, type);
         }
@@ -133,6 +142,15 @@
         }
 
         /**
+         * Return vendor tag id.
+         *
+         * @hide
+         */
+        public long getVendorId() {
+            return mKey.getVendorId();
+        }
+
+        /**
          * {@inheritDoc}
          */
         @Override
@@ -199,6 +217,7 @@
      */
     private CaptureRequest() {
         mSettings = new CameraMetadataNative();
+        setNativeInstance(mSettings);
         mSurfaceSet = new HashSet<Surface>();
         mIsReprocess = false;
         mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
@@ -212,6 +231,7 @@
     @SuppressWarnings("unchecked")
     private CaptureRequest(CaptureRequest source) {
         mSettings = new CameraMetadataNative(source.mSettings);
+        setNativeInstance(mSettings);
         mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
         mIsReprocess = source.mIsReprocess;
         mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -242,6 +262,7 @@
     private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
             int reprocessableSessionId) {
         mSettings = CameraMetadataNative.move(settings);
+        setNativeInstance(mSettings);
         mSurfaceSet = new HashSet<Surface>();
         mIsReprocess = isReprocess;
         if (isReprocess) {
@@ -441,6 +462,7 @@
      */
     private void readFromParcel(Parcel in) {
         mSettings.readFromParcel(in);
+        setNativeInstance(mSettings);
 
         mSurfaceSet.clear();
 
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 3f8b57a..419e3e2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -78,6 +78,15 @@
          *
          * @hide
          */
+        public Key(String name, Class<T> type, long vendorId) {
+            mKey = new CameraMetadataNative.Key<T>(name, type, vendorId);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
         public Key(String name, Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, type);
         }
@@ -110,6 +119,15 @@
         }
 
         /**
+         * Return vendor tag id.
+         *
+         * @hide
+         */
+        public long getVendorId() {
+            return mKey.getVendorId();
+        }
+
+        /**
          * {@inheritDoc}
          */
         @Override
@@ -186,6 +204,7 @@
         if (mResults.isEmpty()) {
             throw new AssertionError("Results must not be empty");
         }
+        setNativeInstance(mResults);
         mRequest = parent;
         mSequenceId = extras.getRequestId();
         mFrameNumber = extras.getFrameNumber();
@@ -215,6 +234,7 @@
             throw new AssertionError("Results must not be empty");
         }
 
+        setNativeInstance(mResults);
         mRequest = null;
         mSequenceId = sequenceId;
         mFrameNumber = -1;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 4d92ab1cc..ebe2fa1 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -79,10 +79,28 @@
     public static class Key<T> {
         private boolean mHasTag;
         private int mTag;
+        private long mVendorId = Long.MAX_VALUE;
         private final Class<T> mType;
         private final TypeReference<T> mTypeReference;
         private final String mName;
         private final int mHash;
+
+        /**
+         * @hide
+         */
+        public Key(String name, Class<T> type, long vendorId) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (type == null) {
+                throw new NullPointerException("Type needs to be non-null");
+            }
+            mName = name;
+            mType = type;
+            mVendorId = vendorId;
+            mTypeReference = TypeReference.createSpecializedTypeReference(type);
+            mHash = mName.hashCode() ^ mTypeReference.hashCode();
+        }
+
         /**
          * Visible for testing only.
          *
@@ -194,7 +212,7 @@
          */
         public final int getTag() {
             if (!mHasTag) {
-                mTag = CameraMetadataNative.getTag(mName);
+                mTag = CameraMetadataNative.getTag(mName, mVendorId);
                 mHasTag = true;
             }
             return mTag;
@@ -212,6 +230,15 @@
         }
 
         /**
+         * Get the vendor tag provider id.
+         *
+         * @hide
+         */
+        public final long getVendorId() {
+            return mVendorId;
+        }
+
+        /**
          * Get the type reference backing the type {@code T} for this key.
          *
          * <p>The distinction is only important if {@code T} is a generic, e.g.
@@ -463,13 +490,14 @@
     }
 
     private <T> T getBase(Key<T> key) {
-        int tag = key.getTag();
+        int tag = nativeGetTagFromKeyLocal(key.getName());
         byte[] values = readValues(tag);
         if (values == null) {
             return null;
         }
 
-        Marshaler<T> marshaler = getMarshalerForKey(key);
+        int nativeType = nativeGetTypeFromTagLocal(tag);
+        Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
         return marshaler.unmarshal(buffer);
     }
@@ -947,15 +975,15 @@
     }
 
     private <T> void setBase(Key<T> key, T value) {
-        int tag = key.getTag();
-
+        int tag = nativeGetTagFromKeyLocal(key.getName());
         if (value == null) {
             // Erase the entry
             writeValues(tag, /*src*/null);
             return;
         } // else update the entry to a new value
 
-        Marshaler<T> marshaler = getMarshalerForKey(key);
+        int nativeType = nativeGetTypeFromTagLocal(tag);
+        Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
         int size = marshaler.calculateMarshalSize(value);
 
         // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
@@ -1092,10 +1120,14 @@
     private native synchronized void nativeWriteValues(int tag, byte[] src);
     private native synchronized void nativeDump() throws IOException; // dump to ALOGD
 
-    private static native ArrayList nativeGetAllVendorKeys(Class keyClass);
-    private static native int nativeGetTagFromKey(String keyName)
+    private native synchronized ArrayList nativeGetAllVendorKeys(Class keyClass);
+    private native synchronized int nativeGetTagFromKeyLocal(String keyName)
             throws IllegalArgumentException;
-    private static native int nativeGetTypeFromTag(int tag)
+    private native synchronized int nativeGetTypeFromTagLocal(int tag)
+            throws IllegalArgumentException;
+    private static native int nativeGetTagFromKey(String keyName, long vendorId)
+            throws IllegalArgumentException;
+    private static native int nativeGetTypeFromTag(int tag, long vendorId)
             throws IllegalArgumentException;
 
     /**
@@ -1133,7 +1165,7 @@
      *
      * @hide
      */
-    public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
+    public <K>  ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
         if (keyClass == null) {
             throw new NullPointerException();
         }
@@ -1149,19 +1181,32 @@
      * @hide
      */
     public static int getTag(String key) {
-        return nativeGetTagFromKey(key);
+        return nativeGetTagFromKey(key, Long.MAX_VALUE);
+    }
+
+    /**
+     * Convert a key string into the equivalent native tag.
+     *
+     * @throws IllegalArgumentException if the key was not recognized
+     * @throws NullPointerException if the key was null
+     *
+     * @hide
+     */
+    public static int getTag(String key, long vendorId) {
+        return nativeGetTagFromKey(key, vendorId);
     }
 
     /**
      * Get the underlying native type for a tag.
      *
      * @param tag An integer tag, see e.g. {@link #getTag}
+     * @param vendorId A vendor tag provider id
      * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
      *
      * @hide
      */
-    public static int getNativeType(int tag) {
-        return nativeGetTypeFromTag(tag);
+    public static int getNativeType(int tag, long vendorId) {
+        return nativeGetTypeFromTag(tag, vendorId);
     }
 
     /**
@@ -1226,9 +1271,9 @@
      * @throws UnsupportedOperationException
      *          if the native/managed type combination for {@code key} is not supported
      */
-    private static <T> Marshaler<T> getMarshalerForKey(Key<T> key) {
+    private static <T> Marshaler<T> getMarshalerForKey(Key<T> key, int nativeType) {
         return MarshalRegistry.getMarshaler(key.getTypeReference(),
-                getNativeType(key.getTag()));
+                nativeType);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
diff --git a/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java
new file mode 100644
index 0000000..1f92f6d
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/VendorTagDescriptorCache.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hardware.camera2.params;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * A class for describing the vendor tag cache declared by a camera HAL module.
+ * Generally only used by the native side of
+ * android.hardware.camera2.impl.CameraMetadataNative
+ *
+ * @hide
+ */
+public final class VendorTagDescriptorCache implements Parcelable {
+
+    private VendorTagDescriptorCache(Parcel source) {
+    }
+
+    public static final Parcelable.Creator<VendorTagDescriptorCache> CREATOR =
+            new Parcelable.Creator<VendorTagDescriptorCache>() {
+        @Override
+        public VendorTagDescriptorCache createFromParcel(Parcel source) {
+            try {
+                VendorTagDescriptorCache vendorDescriptorCache = new VendorTagDescriptorCache(source);
+                return vendorDescriptorCache;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating VendorTagDescriptorCache from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public VendorTagDescriptorCache[] newArray(int size) {
+            return new VendorTagDescriptorCache[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("dest must not be null");
+        }
+    }
+
+    private static final String TAG = "VendorTagDescriptorCache";
+}
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 d6dd28be..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 8349510..4c6d22a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3313,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,
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
new file mode 100644
index 0000000..304ee29
--- /dev/null
+++ b/core/java/android/os/ConfigUpdate.java
@@ -0,0 +1,72 @@
+/*
+ * 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.os;
+
+import android.annotation.SystemApi;
+
+/**
+ * Intents used to provide unbundled updates of system data.
+ * All require the UPDATE_CONFIG permission.
+ *
+ * @see com.android.server.updates
+ * @hide
+ */
+@SystemApi
+public final class ConfigUpdate {
+
+    /**
+     * Update system wide certificate pins for TLS connections.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
+
+    /**
+     * Update system wide Intent firewall.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_INTENT_FIREWALL
+            = "android.intent.action.UPDATE_INTENT_FIREWALL";
+
+    /**
+     * Update list of permium SMS short codes.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_SMS_SHORT_CODES
+            = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+
+    /**
+     * Update list of carrier provisioning URLs.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS
+            = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+
+    /**
+     * Update set of trusted logs used for Certificate Transparency support for TLS connections.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CT_LOGS
+            = "android.intent.action.UPDATE_CT_LOGS";
+
+    private ConfigUpdate() {
+    }
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 9f8a621..aae22c1 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -204,35 +204,21 @@
      * 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
      *     handling this fill request in order to save resources.
      * @param callback object used to notify the result of the request.
      */
-    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data, int flags,
-            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) {
-        //TODO(b/33197203): make non-abstract once older method is removed
-        onFillRequest(structure, data, cancellationSignal, callback);
-    }
-
-    /**
-     * @hide
-     * @deprecated - use {@link #onFillRequest(AssistStructure, Bundle, int,
-     * CancellationSignal, FillCallback)} instead
-     */
-    //TODO(b/33197203): remove once clients are not using anymore
-    @Deprecated
-    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
-            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) {
-        // Should never be called because it was abstract before.
-        throw new UnsupportedOperationException("deprecated");
-    }
+    public abstract void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
+            int flags, @NonNull CancellationSignal cancellationSignal,
+            @NonNull FillCallback callback);
 
     /**
      * Called when user requests service to save the fields of an {@link Activity}.
@@ -242,11 +228,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/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 717312b..3117f98 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -297,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.
          */
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 95608a5..f75b7af 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -129,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;
@@ -222,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:
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index e39d53f..137cf57 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -36,10 +36,6 @@
     private final int mUser;
 
     /**
-     * Data type: {@code String}. See {@link NotificationChannel#getId()}.
-     */
-    public static final String KEY_CHANNEL_ID = "key_channel_id";
-    /**
      * Data type: ArrayList of {@code String}, where each is a representation of a
      * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
      * See {@link android.app.Notification.Builder#addPerson(String)}.
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 46609df..6ec9d69 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -138,69 +138,6 @@
         }
     }
 
-    /**
-     * Creates a notification channel that notifications can be posted to for a given package.
-     *
-     * @param pkg The package to create a channel for.
-     * @param channel  the channel to attempt to create.
-     */
-    public void createNotificationChannel(@NonNull String pkg,
-            @NonNull NotificationChannel channel) {
-        if (!isBound()) return;
-        try {
-            getNotificationInterface().createNotificationChannelFromAssistant(
-                    mWrapper, pkg, channel);
-        } catch (RemoteException e) {
-            Log.v(TAG, "Unable to contact notification manager", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Updates a notification channel for a given package.
-     *
-     * @param pkg The package to the channel belongs to.
-     * @param channel the channel to attempt to update.
-     */
-    public void updateNotificationChannel(@NonNull String pkg,
-            @NonNull NotificationChannel channel) {
-        if (!isBound()) return;
-        try {
-            getNotificationInterface().updateNotificationChannelFromAssistant(
-                    mWrapper, pkg, channel);
-        } catch (RemoteException e) {
-            Log.v(TAG, "Unable to contact notification manager", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Returns all notification channels belonging to the given package.
-     */
-    public List<NotificationChannel> getNotificationChannels(@NonNull String pkg) {
-        if (!isBound()) return null;
-        try {
-            return getNotificationInterface().getNotificationChannelsFromAssistant(
-                    mWrapper, pkg).getList();
-        } catch (RemoteException e) {
-            Log.v(TAG, "Unable to contact notification manager", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Deletes the given notification channel.
-     */
-    public void deleteNotificationChannel(@NonNull String pkg, @NonNull String channelId) {
-        if (!isBound()) return;
-        try {
-            getNotificationInterface().deleteNotificationChannelFromAssistant(
-                    mWrapper, pkg, channelId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
 
     private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
         @Override
diff --git a/core/java/android/service/resolver/IResolverRankerResult.aidl b/core/java/android/service/resolver/IResolverRankerResult.aidl
new file mode 100644
index 0000000..bda3154
--- /dev/null
+++ b/core/java/android/service/resolver/IResolverRankerResult.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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
new file mode 100644
index 0000000..f0d747d
--- /dev/null
+++ b/core/java/android/service/resolver/IResolverRankerService.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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
new file mode 100644
index 0000000..0506747
--- /dev/null
+++ b/core/java/android/service/resolver/ResolverRankerService.java
@@ -0,0 +1,187 @@
+/*
+ * 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.aidl b/core/java/android/service/resolver/ResolverTarget.aidl
new file mode 100644
index 0000000..6cab2d4
--- /dev/null
+++ b/core/java/android/service/resolver/ResolverTarget.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+parcelable ResolverTarget;
diff --git a/core/java/android/service/resolver/ResolverTarget.java b/core/java/android/service/resolver/ResolverTarget.java
new file mode 100644
index 0000000..fb3e2d7
--- /dev/null
+++ b/core/java/android/service/resolver/ResolverTarget.java
@@ -0,0 +1,216 @@
+/*
+ * 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/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 5d8f336..829b2b7 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -494,7 +494,8 @@
      * On TV remotes, switches to viewing live TV. */
     public static final int KEYCODE_TV              = 170;
     /** Key code constant: Window key.
-     * On TV remotes, toggles picture-in-picture mode or other windowing functions. */
+     * On TV remotes, toggles picture-in-picture mode or other windowing functions.
+     * On Android Wear devices, triggers a display offset. */
     public static final int KEYCODE_WINDOW          = 171;
     /** Key code constant: Guide key.
      * On TV remotes, shows a programming guide. */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ffcd95..b4100123 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.
      */
@@ -7423,6 +7431,9 @@
             AccessibilityNodeInfo info = createAccessibilityNodeInfo();
             structure.setChildCount(1);
             ViewStructure root = structure.newChild(0);
+            if (forAutofill) {
+                setAutofillId(root);
+            }
             populateVirtualStructure(root, provider, info, forAutofill);
             info.recycle();
         }
@@ -7440,10 +7451,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 +7461,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 +7475,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 +7508,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 +9155,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 +17159,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 +17241,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 +17629,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 +17930,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 +18007,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 +18692,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 +18847,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 +20207,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}
@@ -24305,7 +24406,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.
@@ -24325,7 +24432,9 @@
          */
         public BaseSavedState(Parcel source, ClassLoader loader) {
             super(source, loader);
+            mSavedData = source.readInt();
             mStartActivityRequestWhoSaved = source.readString();
+            mIsAutofilled = source.readBoolean();
         }
 
         /**
@@ -24340,7 +24449,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
@@ -24741,6 +24853,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/WindowManager.java b/core/java/android/view/WindowManager.java
index 6b8aab6..666ccf4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.KeyguardManager;
 import android.app.Presentation;
 import android.content.Context;
@@ -1278,7 +1279,9 @@
         /**
          * Never animate position changes of the window.
          *
-         * {@hide} */
+         * {@hide}
+         */
+        @TestApi
         public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;
 
         /** Window flag: special flag to limit the size of the window to be
@@ -1387,6 +1390,7 @@
          * Control flags that are private to the platform.
          * @hide
          */
+        @TestApi
         public int privateFlags;
 
         /**
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 7d9253b..58da92f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -57,6 +57,7 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.graphics.fonts.FontVariationAxis;
+import android.icu.text.DecimalFormatSymbols;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -11058,6 +11059,26 @@
             return TextDirectionHeuristics.LTR;
         }
 
+        if (mEditor != null
+                && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
+                    == EditorInfo.TYPE_CLASS_PHONE) {
+            // Phone numbers must be in the direction of the locale's digits. Most locales have LTR
+            // digits, but some locales, such as those written in the Adlam or N'Ko scripts, have
+            // RTL digits.
+            final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(getTextLocale());
+            final String zero = symbols.getDigitStrings()[0];
+            // In case the zero digit is multi-codepoint, just use the first codepoint to determine
+            // direction.
+            final int firstCodepoint = zero.codePointAt(0);
+            final byte digitDirection = Character.getDirectionality(firstCodepoint);
+            if (digitDirection == Character.DIRECTIONALITY_RIGHT_TO_LEFT
+                    || digitDirection == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) {
+                return TextDirectionHeuristics.RTL;
+            } else {
+                return TextDirectionHeuristics.LTR;
+            }
+        }
+
         // Always need to resolve layout direction first
         final boolean defaultIsRtl = (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
 
diff --git a/core/java/com/android/internal/app/LRResolverRankerService.java b/core/java/com/android/internal/app/LRResolverRankerService.java
new file mode 100644
index 0000000..1cad7c7
--- /dev/null
+++ b/core/java/com/android/internal/app/LRResolverRankerService.java
@@ -0,0 +1,199 @@
+/*
+ * 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 3f1c9ad..622b708 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -530,6 +530,9 @@
             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 096fcb8..73b62a5 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -26,20 +26,34 @@
 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;
@@ -61,11 +75,15 @@
 
     private static final float RECENCY_MULTIPLIER = 2.f;
 
-    // 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";
+    // 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;
 
     private final Collator mCollator;
     private final boolean mHttp;
@@ -74,18 +92,74 @@
     private final Map<String, UsageStats> mStats;
     private final long mCurrentTime;
     private final long mSinceTime;
-    private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
+    private final LinkedHashMap<ComponentName, ResolverTarget> mTargetsDict = 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 LogisticRegressionAppRanker mRanker;
+    private IResolverRankerService mRanker;
+    private ResolverRankerServiceConnection mConnection;
+    private AfterCompute mAfterCompute;
+    private Context mContext;
+    private CountDownLatch mConnectSignal;
 
-    public ResolverComparator(Context context, Intent intent, String referrerPackage) {
+    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) {
         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);
@@ -96,9 +170,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);
@@ -114,20 +188,24 @@
         }
     }
 
+    public void setCallBack(AfterCompute afterCompute) {
+        mAfterCompute = afterCompute;
+    }
+
+    // compute features for each target according to usage stats of targets.
     public void compute(List<ResolvedComponentInfo> targets) {
-        mScoredTargets.clear();
+        reset();
 
         final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
 
-        long mostRecentlyUsedTime = recentSinceTime + 1;
-        long mostTimeSpent = 1;
-        int mostLaunched = 1;
-        int mostSelected = 1;
+        float mostRecencyScore = 1.0f;
+        float mostTimeSpentScore = 1.0f;
+        float mostLaunchScore = 1.0f;
+        float mostChooserScore = 1.0f;
 
         for (ResolvedComponentInfo target : targets) {
-            final ScoredTarget scoredTarget
-                    = new ScoredTarget(target.getResolveInfoAt(0).activityInfo);
-            mScoredTargets.put(target.name, scoredTarget);
+            final ResolverTarget resolverTarget = new ResolverTarget();
+            mTargetsDict.put(target.name, resolverTarget);
             final UsageStats pkStats = mStats.get(target.name.getPackageName());
             if (pkStats != null) {
                 // Only count recency for apps that weren't the caller
@@ -135,31 +213,33 @@
                 // Persistent processes muck this up, so omit them too.
                 if (!target.name.getPackageName().equals(mReferrerPackage)
                         && !isPersistentProcess(target)) {
-                    final long lastTimeUsed = pkStats.getLastTimeUsed();
-                    scoredTarget.lastTimeUsed = lastTimeUsed;
-                    if (lastTimeUsed > mostRecentlyUsedTime) {
-                        mostRecentlyUsedTime = lastTimeUsed;
+                    final float recencyScore =
+                            (float) Math.max(pkStats.getLastTimeUsed() - recentSinceTime, 0);
+                    resolverTarget.setRecencyScore(recencyScore);
+                    if (recencyScore > mostRecencyScore) {
+                        mostRecencyScore = recencyScore;
                     }
                 }
-                final long timeSpent = pkStats.getTotalTimeInForeground();
-                scoredTarget.timeSpent = timeSpent;
-                if (timeSpent > mostTimeSpent) {
-                    mostTimeSpent = timeSpent;
+                final float timeSpentScore = (float) pkStats.getTotalTimeInForeground();
+                resolverTarget.setTimeSpentScore(timeSpentScore);
+                if (timeSpentScore > mostTimeSpentScore) {
+                    mostTimeSpentScore = timeSpentScore;
                 }
-                final int launched = pkStats.mLaunchCount;
-                scoredTarget.launchCount = launched;
-                if (launched > mostLaunched) {
-                    mostLaunched = launched;
+                final float launchScore = (float) pkStats.mLaunchCount;
+                resolverTarget.setLaunchScore(launchScore);
+                if (launchScore > mostLaunchScore) {
+                    mostLaunchScore = launchScore;
                 }
 
-                int selected = 0;
+                float chooserScore = 0.0f;
                 if (pkStats.mChooserCounts != null && mAction != null
                         && pkStats.mChooserCounts.get(mAction) != null) {
-                    selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
+                    chooserScore = (float) pkStats.mChooserCounts.get(mAction)
+                            .getOrDefault(mContentType, 0);
                     if (mAnnotations != null) {
                         final int size = mAnnotations.length;
                         for (int i = 0; i < size; i++) {
-                            selected += pkStats.mChooserCounts.get(mAction)
+                            chooserScore += (float) pkStats.mChooserCounts.get(mAction)
                                     .getOrDefault(mAnnotations[i], 0);
                         }
                     }
@@ -169,44 +249,37 @@
                         Log.d(TAG, "Action type is null");
                     } else {
                         Log.d(TAG, "Chooser Count of " + mAction + ":" +
-                                target.name.getPackageName() + " is " + Integer.toString(selected));
+                                target.name.getPackageName() + " is " +
+                                Float.toString(chooserScore));
                     }
                 }
-                scoredTarget.chooserCount = selected;
-                if (selected > mostSelected) {
-                    mostSelected = selected;
+                resolverTarget.setChooserScore(chooserScore);
+                if (chooserScore > mostChooserScore) {
+                    mostChooserScore = chooserScore;
                 }
             }
         }
 
-
         if (DEBUG) {
-            Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime
-                    + " mostTimeSpent: " + mostTimeSpent
-                    + " recentSinceTime: " + recentSinceTime
-                    + " mostLaunched: " + mostLaunched);
+            Log.d(TAG, "compute - mostRecencyScore: " + mostRecencyScore
+                    + " mostTimeSpentScore: " + mostTimeSpentScore
+                    + " mostLaunchScore: " + mostLaunchScore
+                    + " mostChooserScore: " + mostChooserScore);
         }
 
-        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());
+        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);
             if (DEBUG) {
                 Log.d(TAG, "Scores: " + target);
             }
         }
-    }
-
-    static boolean isPersistentProcess(ResolvedComponentInfo rci) {
-        if (rci != null && rci.getCount() > 0) {
-            return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
-                    ApplicationInfo.FLAG_PERSISTENT) != 0;
-        }
-        return false;
+        predictSelectProbabilities(mTargets);
     }
 
     @Override
@@ -245,16 +318,16 @@
         // Pinned items stay stable within a normal lexical sort and ignore scoring.
         if (!lPinned && !rPinned) {
             if (mStats != null) {
-                final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName(
+                final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName(
                         lhs.activityInfo.packageName, lhs.activityInfo.name));
-                final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
+                final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
                         rhs.activityInfo.packageName, rhs.activityInfo.name));
 
-                final int selectProbDiff = Float.compare(
-                        rhsTarget.selectProb, lhsTarget.selectProb);
+                final int selectProbabilityDiff = Float.compare(
+                        rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
 
-                if (selectProbDiff != 0) {
-                    return selectProbDiff > 0 ? 1 : -1;
+                if (selectProbabilityDiff != 0) {
+                    return selectProbabilityDiff > 0 ? 1 : -1;
                 }
             }
         }
@@ -268,177 +341,234 @@
     }
 
     public float getScore(ComponentName name) {
-        final ScoredTarget target = mScoredTargets.get(name);
+        final ResolverTarget target = mTargetsDict.get(name);
         if (target != null) {
-            return target.selectProb;
+            return target.getSelectProbability();
         }
         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) {
-        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);
+        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.");
+                }
             }
         }
-        mRanker.commitUpdate();
     }
 
-    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";
-
-        // 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();
+    // 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();
         }
-
-        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)));
+        if (DEBUG) {
+            Log.d(TAG, "Unbinded Resolver Ranker.");
         }
+    }
 
-        public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
-            if (target == null) {
+    // 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.");
+                }
                 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);
+        }
+        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;
             }
             if (DEBUG) {
-                Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
+                Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName);
             }
+            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 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));
+        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);
+                }
             }
-            editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
-            editor.apply();
-        }
+        };
 
-        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.
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) {
-                Log.d(TAG, "Context Package Name: " + context.getPackageName());
+                Log.d(TAG, "onServiceConnected: " + name);
             }
-            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);
+            synchronized (mLock) {
+                mRanker = IResolverRankerService.Stub.asInterface(service);
+                mConnectSignal.countDown();
+            }
         }
 
-        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));
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) {
+                Log.d(TAG, "onServiceDisconnected: " + name);
+            }
+            synchronized (mLock) {
+                destroy();
             }
         }
+
+        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 4071ff4..e8bebb7 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -32,8 +32,10 @@
 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;
 
 /**
@@ -205,14 +207,42 @@
         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);
+            mResolverComparator =
+                    new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, callback);
+        } else {
+            mResolverComparator.setCallBack(callback);
         }
-        mResolverComparator.compute(inputList);
-        Collections.sort(inputList, mResolverComparator);
+        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);
+        }
     }
 
     private static boolean isSameResolvedComponent(ResolveInfo a,
@@ -233,7 +263,7 @@
     @VisibleForTesting
     public float getScore(ResolverActivity.DisplayResolveInfo target) {
         if (mResolverComparator == null) {
-            mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+            return 0.0f;
         }
         return mResolverComparator.getScore(target.getResolvedComponentName());
     }
@@ -249,4 +279,10 @@
             mResolverComparator.updateChooserCounts(packageName, userId, action);
         }
     }
+
+    public void destroy() {
+        if (mResolverComparator != null) {
+            mResolverComparator.destroy();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index a70209c..1abb59b 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -74,7 +74,6 @@
     private final Rect mOldStableInsets = new Rect();
     private final Rect mSystemInsets = new Rect();
     private final Rect mStableInsets = new Rect();
-    private final Rect mTmpRect = new Rect();
 
     public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
             Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
@@ -371,6 +370,12 @@
         DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height);
         mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
         final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
+        final int bottomInset = DecorView.getColorViewBottomInset(stableInsets.bottom,
+                systemInsets.bottom);
+        final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
+                systemInsets.right);
+        final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left,
+                systemInsets.left);
         if (mStatusBarColor != null) {
             mStatusBarColor.setBounds(0, 0, left + width, topInset);
             mStatusBarColor.draw(canvas);
@@ -380,8 +385,14 @@
         // don't want the navigation bar background be moving around when resizing in docked mode.
         // However, we need it for the transitions into/out of docked mode.
         if (mNavigationBarColor != null && fullscreen) {
-            DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect);
-            mNavigationBarColor.setBounds(mTmpRect);
+            final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
+            if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
+                mNavigationBarColor.setBounds(width - size, 0, width, height);
+            } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) {
+                mNavigationBarColor.setBounds(0, 0, size, height);
+            } else {
+                mNavigationBarColor.setBounds(0, height - size, width, height);
+            }
             mNavigationBarColor.draw(canvas);
         }
         mSystemBarBackgroundNode.end(canvas);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 653796d..a8e16c9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -119,21 +119,6 @@
     // The height of a window which has not in DIP.
     private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
 
-    public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
-            new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
-                    Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
-                    Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
-                    com.android.internal.R.id.statusBarBackground,
-                    FLAG_FULLSCREEN);
-
-    public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
-            new ColorViewAttributes(
-                    SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
-                    Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
-                    Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
-                    com.android.internal.R.id.navigationBarBackground,
-                    0 /* hideWindowFlag */);
-
     // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
     // size calculation takes the shadow size into account. We set the elevation currently
     // to max until the first layout command has been executed.
@@ -177,10 +162,18 @@
     // View added at runtime to draw under the navigation bar area
     private View mNavigationGuard;
 
-    private final ColorViewState mStatusColorViewState =
-            new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
-    private final ColorViewState mNavigationColorViewState =
-            new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
+    private final ColorViewState mStatusColorViewState = new ColorViewState(
+            SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+            Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
+            Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
+            com.android.internal.R.id.statusBarBackground,
+            FLAG_FULLSCREEN);
+    private final ColorViewState mNavigationColorViewState = new ColorViewState(
+            SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+            Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
+            Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
+            com.android.internal.R.id.navigationBarBackground,
+            0 /* hideWindowFlag */);
 
     private final Interpolator mShowInterpolator;
     private final Interpolator mHideInterpolator;
@@ -990,50 +983,35 @@
         return false;
     }
 
-    public static int getColorViewTopInset(int stableTop, int systemTop) {
+    static int getColorViewTopInset(int stableTop, int systemTop) {
         return Math.min(stableTop, systemTop);
     }
 
-    public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
+    static int getColorViewBottomInset(int stableBottom, int systemBottom) {
         return Math.min(stableBottom, systemBottom);
     }
 
-    public static int getColorViewRightInset(int stableRight, int systemRight) {
+    static int getColorViewRightInset(int stableRight, int systemRight) {
         return Math.min(stableRight, systemRight);
     }
 
-    public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
+    static int getColorViewLeftInset(int stableLeft, int systemLeft) {
         return Math.min(stableLeft, systemLeft);
     }
 
-    public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
+    static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
         return bottomInset == 0 && rightInset > 0;
     }
 
-    public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
+    static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
         return bottomInset == 0 && leftInset > 0;
     }
 
-    public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
+    static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
         return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
                 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
     }
 
-    public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
-            Rect contentInsets, Rect outRect) {
-        final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
-        final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
-        final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
-        final int size = getNavBarSize(bottomInset, rightInset, leftInset);
-        if (isNavBarToRightEdge(bottomInset, rightInset)) {
-            outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
-        } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
-            outRect.set(0, 0, size, canvasHeight);
-        } else {
-            outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
-        }
-    }
-
     WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
         WindowManager.LayoutParams attrs = mWindow.getAttributes();
         int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
@@ -1153,14 +1131,9 @@
     }
 
     private int calculateStatusBarColor() {
-        return calculateStatusBarColor(mWindow.getAttributes().flags,
-                mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
-    }
-
-    public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
-            int statusBarColor) {
-        return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
-                : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
+        int flags = mWindow.getAttributes().flags;
+        return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? mSemiTransparentStatusBarColor
+                : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? mWindow.mStatusBarColor
                 : Color.BLACK;
     }
 
@@ -1187,9 +1160,13 @@
     private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
             int size, boolean verticalBar, boolean seascape, int sideMargin,
             boolean animate, boolean force) {
-        state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
-        boolean show = state.attributes.isVisible(state.present, color,
-                mWindow.getAttributes().flags, force);
+        state.present = (sysUiVis & state.systemUiHideFlag) == 0
+                && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
+                && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+                        || force);
+        boolean show = state.present
+                && (color & Color.BLACK) != 0
+                && ((mWindow.getAttributes().flags & state.translucentFlag) == 0  || force);
         boolean showView = show && !isResizing() && size > 0;
 
         boolean visibilityChanged = false;
@@ -1198,15 +1175,15 @@
         int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
         int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
         int resolvedGravity = verticalBar
-                ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
-                : state.attributes.verticalGravity;
+                ? (seascape ? state.seascapeGravity : state.horizontalGravity)
+                : state.verticalGravity;
 
         if (view == null) {
             if (showView) {
                 state.view = view = new View(mContext);
                 view.setBackgroundColor(color);
-                view.setTransitionName(state.attributes.transitionName);
-                view.setId(state.attributes.id);
+                view.setTransitionName(state.transitionName);
+                view.setId(state.id);
                 visibilityChanged = true;
                 view.setVisibility(INVISIBLE);
                 state.targetVisibility = VISIBLE;
@@ -2292,15 +2269,6 @@
         boolean visible;
         int color;
 
-        final ColorViewAttributes attributes;
-
-        ColorViewState(ColorViewAttributes attributes) {
-            this.attributes = attributes;
-        }
-    }
-
-    public static class ColorViewAttributes {
-
         final int id;
         final int systemUiHideFlag;
         final int translucentFlag;
@@ -2310,9 +2278,9 @@
         final String transitionName;
         final int hideWindowFlag;
 
-        private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
-                int horizontalGravity, int seascapeGravity, String transitionName, int id,
-                int hideWindowFlag) {
+        ColorViewState(int systemUiHideFlag,
+                int translucentFlag, int verticalGravity, int horizontalGravity,
+                int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
             this.id = id;
             this.systemUiHideFlag = systemUiHideFlag;
             this.translucentFlag = translucentFlag;
@@ -2322,24 +2290,6 @@
             this.transitionName = transitionName;
             this.hideWindowFlag = hideWindowFlag;
         }
-
-        public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
-            return (sysUiVis & systemUiHideFlag) == 0
-                    && (windowFlags & hideWindowFlag) == 0
-                    && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
-                    || force);
-        }
-
-        public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
-            return present
-                    && (color & Color.BLACK) != 0
-                    && ((windowFlags & translucentFlag) == 0  || force);
-        }
-
-        public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
-            final boolean present = isPresent(sysUiVis, windowFlags, force);
-            return isVisible(present, color, windowFlags, force);
-        }
     }
 
     /**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index da5d04d..33fabfc 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -208,6 +208,7 @@
     $(TOP)/system/core/include \
     $(TOP)/system/core/libappfuse/include \
     $(TOP)/system/media/camera/include \
+    $(TOP)/system/media/private/camera/include \
     $(TOP)/system/netd/include \
     external/giflib \
     external/pdfium/public \
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index 7b381b4..bfb25113 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -44,7 +44,7 @@
     mWidth = width;
     mHeight = height;
     SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height));
-    return Canvas::create_canvas(canvas);
+    return Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer);
 }
 
 void Picture::endRecording() {
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index d233f7b..abc3599 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -21,6 +21,7 @@
 
 #include "CreateJavaOutputStreamAdaptor.h"
 
+#include "SkColorSpaceXformCanvas.h"
 #include "SkDocument.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -94,8 +95,10 @@
 
             SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
                     &(page->mContentRect));
+            std::unique_ptr<SkCanvas> toSRGBCanvas =
+                    SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
 
-            canvas->drawPicture(page->mPicture);
+            toSRGBCanvas->drawPicture(page->mPicture);
 
             document->endPage();
         }
@@ -128,7 +131,7 @@
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
     SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
             contentLeft, contentTop, contentRight, contentBottom);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer));
 }
 
 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
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/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 78a5735..c11ce0f 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -36,6 +36,7 @@
 #include <android/hardware/ICameraService.h>
 #include <binder/IServiceManager.h>
 #include <camera/CameraMetadata.h>
+#include <camera_metadata_hidden.h>
 #include <camera/VendorTagDescriptor.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
@@ -162,8 +163,10 @@
 extern "C" {
 
 static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType);
-static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
-static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName, jlong vendorId);
+static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId);
+static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag);
 static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz);
 
 // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
@@ -286,7 +289,9 @@
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
     if (metadata == NULL) return NULL;
 
-    int tagType = get_camera_metadata_tag_type(tag);
+    const camera_metadata_t *metaBuffer = metadata->getAndLock();
+    int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer);
+    metadata->unlock(metaBuffer);
     if (tagType == -1) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                              "Tag (%d) did not have a type", tag);
@@ -323,7 +328,9 @@
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
     if (metadata == NULL) return;
 
-    int tagType = get_camera_metadata_tag_type(tag);
+    const camera_metadata_t *metaBuffer = metadata->getAndLock();
+    int tagType = get_local_camera_metadata_tag_type(tag, metaBuffer);
+    metadata->unlock(metaBuffer);
     if (tagType == -1) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                              "Tag (%d) did not have a type", tag);
@@ -528,14 +535,11 @@
 
 static const JNINativeMethod gCameraMetadataMethods[] = {
 // static methods
-  { "nativeGetAllVendorKeys",
-    "(Ljava/lang/Class;)Ljava/util/ArrayList;",
-    (void *)CameraMetadata_getAllVendorKeys},
   { "nativeGetTagFromKey",
-    "(Ljava/lang/String;)I",
+    "(Ljava/lang/String;J)I",
     (void *)CameraMetadata_getTagFromKey },
   { "nativeGetTypeFromTag",
-    "(I)I",
+    "(IJ)I",
     (void *)CameraMetadata_getTypeFromTag },
   { "nativeSetupGlobalVendorTagDescriptor",
     "()I",
@@ -559,6 +563,12 @@
   { "nativeSwap",
     "(L" CAMERA_METADATA_CLASS_NAME ";)V",
     (void *)CameraMetadata_swap },
+  { "nativeGetTagFromKeyLocal",
+    "(Ljava/lang/String;)I",
+    (void *)CameraMetadata_getTagFromKeyLocal },
+  { "nativeGetTypeFromTagLocal",
+    "(I)I",
+    (void *)CameraMetadata_getTypeFromTagLocal },
   { "nativeReadValues",
     "(I)[B",
     (void *)CameraMetadata_readValues },
@@ -568,6 +578,9 @@
   { "nativeDump",
     "()V",
     (void *)CameraMetadata_dump },
+  { "nativeGetAllVendorKeys",
+    "(Ljava/lang/Class;)Ljava/util/ArrayList;",
+    (void *)CameraMetadata_getAllVendorKeys},
 // Parcelable interface
   { "nativeReadFromParcel",
     "(Landroid/os/Parcel;)V",
@@ -590,11 +603,11 @@
     gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz);
     gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env,
             gMetadataOffsets.mCharacteristicsKey, "<init>",
-            "(Ljava/lang/String;Ljava/lang/Class;)V");
+            "(Ljava/lang/String;Ljava/lang/Class;J)V");
     gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env,
-            gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+            gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V");
     gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env,
-            gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V");
+            gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;J)V");
 
     // Store global references for primitive array types used by Keys
     jclass byteClazz = FindClassOrDie(env, "[B");
@@ -630,13 +643,76 @@
 
 extern "C" {
 
-static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+static jint CameraMetadata_getTypeFromTagLocal(JNIEnv *env, jobject thiz, jint tag) {
+    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+    metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
+    if (metadata) {
+        const camera_metadata_t *metaBuffer = metadata->getAndLock();
+        vendorId = get_camera_metadata_vendor_id(metaBuffer);
+        metadata->unlock(metaBuffer);
+    }
 
+    int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
+    if (tagType == -1) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Tag (%d) did not have a type", tag);
+        return -1;
+    }
+
+    return tagType;
+}
+
+static jint CameraMetadata_getTagFromKeyLocal(JNIEnv *env, jobject thiz, jstring keyName) {
+    ScopedUtfChars keyScoped(env, keyName);
+    const char *key = keyScoped.c_str();
+    if (key == NULL) {
+        // exception thrown by ScopedUtfChars
+        return 0;
+    }
+    ALOGV("%s (key = '%s')", __FUNCTION__, key);
+
+    uint32_t tag = 0;
+    sp<VendorTagDescriptor> vTags;
+    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
+    if (metadata) {
+        sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get()) {
+            const camera_metadata_t *metaBuffer = metadata->getAndLock();
+            metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer);
+            metadata->unlock(metaBuffer);
+            cache->getVendorTagDescriptor(vendorId, &vTags);
+        }
+    }
+
+    status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
+    if (res != OK) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Could not find tag for key '%s')", key);
+    }
+    return tag;
+}
+
+static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) {
+    metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
     // Get all vendor tags
     sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
     if (vTags.get() == nullptr) {
-        // No vendor tags.
-        return NULL;
+        sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get() == nullptr) {
+            // No vendor tags.
+            return nullptr;
+        }
+
+        CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+        if (metadata == NULL) return NULL;
+
+        const camera_metadata_t *metaBuffer = metadata->getAndLock();
+        vendorId = get_camera_metadata_vendor_id(metaBuffer);
+        cache->getVendorTagDescriptor(vendorId, &vTags);
+        metadata->unlock(metaBuffer);
+        if (vTags.get() == nullptr) {
+            return nullptr;
+        }
     }
 
     int count = vTags->getTagCount();
@@ -714,7 +790,7 @@
                 return NULL;
         }
 
-        jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz);
+        jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz, vendorId);
         if (env->ExceptionCheck()) {
             return NULL;
         }
@@ -731,8 +807,8 @@
     return arrayList;
 }
 
-static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
-
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName,
+        jlong vendorId) {
     ScopedUtfChars keyScoped(env, keyName);
     const char *key = keyScoped.c_str();
     if (key == NULL) {
@@ -744,6 +820,13 @@
     uint32_t tag = 0;
     sp<VendorTagDescriptor> vTags =
             VendorTagDescriptor::getGlobalVendorTagDescriptor();
+    if (vTags.get() == nullptr) {
+        sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get() != nullptr) {
+            cache->getVendorTagDescriptor(vendorId, &vTags);
+        }
+    }
+
     status_t res = CameraMetadata::getTagFromName(key, vTags.get(), &tag);
     if (res != OK) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
@@ -752,8 +835,8 @@
     return tag;
 }
 
-static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
-    int tagType = get_camera_metadata_tag_type(tag);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag, jlong vendorId) {
+    int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
     if (tagType == -1) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                              "Tag (%d) did not have a type", tag);
@@ -787,8 +870,24 @@
                 __FUNCTION__, res.toString8().string());
         return res.serviceSpecificErrorCode();
     }
+    if (0 < desc->getTagCount()) {
+        err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
+    } else {
+        sp<VendorTagDescriptorCache> cache = new VendorTagDescriptorCache();
+        binder::Status res = cameraService->getCameraVendorTagCache(/*out*/cache.get());
+        if (res.serviceSpecificErrorCode() == hardware::ICameraService::ERROR_DISCONNECTED) {
+            // No camera module available, not an error on devices with no cameras
+            VendorTagDescriptorCache::clearGlobalVendorTagCache();
+            return OK;
+        } else if (!res.isOk()) {
+            VendorTagDescriptorCache::clearGlobalVendorTagCache();
+            ALOGE("%s: Failed to setup vendor tag cache: %s",
+                    __FUNCTION__, res.toString8().string());
+            return res.serviceSpecificErrorCode();
+        }
 
-    err = VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
+        err = VendorTagDescriptorCache::setAsGlobalVendorTagCache(cache);
+    }
 
     if (err != OK) {
         return hardware::ICameraService::ERROR_INVALID_OPERATION;
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 15b2f35..dcb2300 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -278,7 +278,8 @@
         jstring ifaceNameObj,
         jstring serviceNameObj) {
 
-    using ::android::vintf::operator<<;
+    using ::android::hidl::base::V1_0::IBase;
+    using ::android::hidl::manager::V1_0::IServiceManager;
 
     if (ifaceNameObj == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", NULL);
@@ -320,13 +321,20 @@
               << "/"
               << serviceName;
 
-    ::android::vintf::Transport transport =
-            ::android::hardware::getTransport(ifaceName, serviceName);
-    if (   transport != ::android::vintf::Transport::EMPTY
-        && transport != ::android::vintf::Transport::HWBINDER) {
+    Return<IServiceManager::Transport> transportRet =
+            manager->getTransport(ifaceNameHStr, serviceNameHStr);
+
+    if (!transportRet.isOk()) {
+        signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+        return NULL;
+    }
+
+    IServiceManager::Transport transport = transportRet;
+
+    if (   transport != IServiceManager::Transport::EMPTY
+        && transport != IServiceManager::Transport::HWBINDER) {
         LOG(ERROR) << "service " << ifaceName << " declares transport method "
-                   << transport << " but framework expects "
-                   << ::android::vintf::Transport::HWBINDER;
+                   << toString(transport) << " but framework expects hwbinder.";
         signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
         return NULL;
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7922250..cf6f37f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -914,7 +914,7 @@
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_answerPhoneCalls"
         android:description="@string/permdesc_answerPhoneCalls"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous|runtime" />
 
 
     <!-- ====================================================================== -->
@@ -3130,6 +3130,15 @@
     <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.
@@ -3641,6 +3650,13 @@
                  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: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/values/attrs.xml b/core/res/res/values/attrs.xml
index d26d952..69c6fa4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8559,11 +8559,6 @@
         <!-- @hide From Theme.colorBackground, used for the TaskDescription background
                    color. -->
         <attr name="colorBackground" />
-        <!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->
-        <attr name="statusBarColor"/>
-        <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
-                   color. -->
-        <attr name="navigationBarColor"/>
     </declare-styleable>
 
     <declare-styleable name="Shortcut">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ed5a42b..3e4b66d7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -245,6 +245,10 @@
         <!-- Additional flag from base permission type: this permission can be granted to ephemeral
              apps -->
         <flag name="ephemeral" value="0x1000" />
+        <!-- Additional flag from base permission type: this permission can only be granted to apps
+             that target runtime permissions ({@link android.os.Build.VERSION_CODES#M} and above)
+             -->
+        <flag name="runtime" value="0x2000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
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/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 bf2f355..ae28797 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4585,6 +4585,9 @@
     <!-- Accessibility title for the autofill dialog used to select a list of options to autofill an activity. [CHAR LIMIT=NONE] -->
     <string name="autofill_picker_accessibility_title">Autofill options</string>
 
+    <!-- Toast message shown when user manually request autofill but service could not figure out the data that would autofill the screen contents. [CHAR LIMIT=NONE] -->
+    <string name="autofill_error_cannot_autofill">Contents can\u2019t be autofilled</string>
+
     <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved
          by an autofill service, but the service does not know what the activity represents [CHAR LIMIT=NONE] -->
     <string name="autofill_save_title">Save to <xliff:g id="label" example="MyPass">%1$s</xliff:g>?</string>
@@ -4602,6 +4605,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/symbols.xml b/core/res/res/values/symbols.xml
index c12116a..d0057d4 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" />
@@ -2874,6 +2875,7 @@
   <java-symbol type="id" name="autofill_save_no" />
   <java-symbol type="id" name="autofill_save_yes" />
   <java-symbol type="id" name="autofill_save_close" />
+  <java-symbol type="string" name="autofill_error_cannot_autofill" />
   <java-symbol type="string" name="autofill" />
   <java-symbol type="string" name="autofill_picker_accessibility_title " />
   <java-symbol type="string" name="autofill_save_title" />
@@ -2883,6 +2885,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/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index e03dcf3..e61d467 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -3530,7 +3530,7 @@
          *
          * @see RenderIntent
          */
-        public RenderIntent getIntent() {
+        public RenderIntent getRenderIntent() {
             return mIntent;
         }
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index daf14af..13d7e09 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -23,6 +23,7 @@
 #include "hwui/MinikinUtils.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 
+#include <SkColorSpaceXformCanvas.h>
 #include <SkDrawable.h>
 #include <SkDeque.h>
 #include <SkDrawFilter.h>
@@ -44,18 +45,22 @@
     return new SkiaCanvas(bitmap);
 }
 
-Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
-    return new SkiaCanvas(skiaCanvas);
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB) {
+    return new SkiaCanvas(skiaCanvas, xformToSRGB);
 }
 
 SkiaCanvas::SkiaCanvas() {}
 
-SkiaCanvas::SkiaCanvas(SkCanvas* canvas)
-    : mCanvas(canvas) {}
+SkiaCanvas::SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB)
+    : mCanvas(canvas)
+{
+    LOG_ALWAYS_FATAL_IF(XformToSRGB::kImmediate == xformToSRGB);
+}
 
 SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
     mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
-    mCanvas = mCanvasOwned.get();
+    mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), SkColorSpace::MakeSRGB());
+    mCanvas = mCanvasWrapper.get();
 }
 
 SkiaCanvas::~SkiaCanvas() {}
@@ -92,19 +97,22 @@
 };
 
 void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
-    SkCanvas* newCanvas = new SkCanvas(bitmap);
+    std::unique_ptr<SkCanvas> newCanvas = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+    std::unique_ptr<SkCanvas> newCanvasWrapper =
+            SkCreateColorSpaceXformCanvas(newCanvas.get(), SkColorSpace::MakeSRGB());
 
     if (!bitmap.isNull()) {
         // Copy the canvas matrix & clip state.
-        newCanvas->setMatrix(mCanvas->getTotalMatrix());
+        newCanvasWrapper->setMatrix(mCanvas->getTotalMatrix());
 
-        ClipCopier copier(newCanvas);
+        ClipCopier copier(newCanvasWrapper.get());
         mCanvas->replayClips(&copier);
     }
 
     // deletes the previously owned canvas (if any)
-    mCanvasOwned = std::unique_ptr<SkCanvas>(newCanvas);
-    mCanvas = newCanvas;
+    mCanvasOwned = std::move(newCanvas);
+    mCanvasWrapper = std::move(newCanvasWrapper);
+    mCanvas = mCanvasWrapper.get();
 
     // clean up the old save stack
     mSaveStack.reset(nullptr);
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 34c3717..13f979cf 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -37,8 +37,12 @@
      *  @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
      *      not be NULL. This constructor does not take ownership, so the caller
      *      must guarantee that it remains valid while the SkiaCanvas is valid.
+     *  @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
+     *      color space before drawing.  This makes sense for software rendering.
+     *      For the picture case, it may make more sense to leave bitmaps as is,
+     *      and handle the xform when replaying the picture.
      */
-    explicit SkiaCanvas(SkCanvas* canvas);
+    explicit SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB);
 
     virtual ~SkiaCanvas();
 
@@ -181,6 +185,7 @@
 
     class Clip;
 
+    std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas
     std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated
     SkCanvas*                 mCanvas;    // we do NOT own this canvas, it must survive us
                                           // unless it is the same as mCanvasOwned.get()
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 8b71086..959059f 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -227,10 +227,16 @@
         *outType = GL_UNSIGNED_BYTE;
         break;
     case kRGBA_F16_SkColorType:
-        // This format is always linear
-        *outFormat = GL_RGBA;
-        *outInternalFormat = GL_RGBA16F;
-        *outType = GL_HALF_FLOAT;
+        if (caches.extensions().getMajorGlVersion() >= 3) {
+            // This format is always linear
+            *outFormat = GL_RGBA;
+            *outInternalFormat = GL_RGBA16F;
+            *outType = GL_HALF_FLOAT;
+        } else {
+            *outFormat = GL_RGBA;
+            *outInternalFormat = caches.rgbaInternalFormat(true);
+            *outType = GL_UNSIGNED_BYTE;
+        }
         break;
     default:
         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
@@ -244,8 +250,17 @@
     rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
             bitmap.info().alphaType(), hasLinearBlending ? sRGB : nullptr));
     rgbaBitmap.eraseColor(0);
-    SkCanvas canvas(rgbaBitmap);
-    canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+
+    if (bitmap.colorType() == kRGBA_F16_SkColorType) {
+        // Drawing RGBA_F16 onto ARGB_8888 is not supported
+        bitmap.readPixels(rgbaBitmap.info()
+                .makeColorSpace(SkColorSpace::MakeSRGB()),
+                rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0);
+    } else {
+        SkCanvas canvas(rgbaBitmap);
+        canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+    }
+
     return rgbaBitmap;
 }
 
@@ -254,7 +269,9 @@
         || info.colorType() == kIndex_8_SkColorType
         || (info.colorType() == kRGB_565_SkColorType
                 && hasLinearBlending
-                && info.colorSpace()->isSRGB());
+                && info.colorSpace()->isSRGB())
+        || (info.colorType() == kRGBA_F16_SkColorType
+                && Caches::getInstance().extensions().getMajorGlVersion() < 3);
 }
 
 void Texture::upload(Bitmap& bitmap) {
@@ -287,10 +304,16 @@
     colorTypeToGlFormatAndType(mCaches, bitmap.colorType(),
             needSRGB && hasLinearBlending, &internalFormat, &format, &type);
 
+    // Some devices don't support GL_RGBA16F, so we need to compare the color type
+    // and internal GL format to decide what to do with 16 bit bitmaps
+    bool rgba16fNeedsConversion = bitmap.colorType() == kRGBA_F16_SkColorType
+            && internalFormat != GL_RGBA16F;
+
     mConnector.reset();
 
     // RGBA16F is always extended sRGB, alpha masks don't have color profiles
-    if (internalFormat != GL_RGBA16F && internalFormat != GL_ALPHA) {
+    // If an RGBA16F bitmap needs conversion, we know the target will be sRGB
+    if (internalFormat != GL_RGBA16F && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) {
         SkColorSpace* colorSpace = bitmap.info().colorSpace();
         // If the bitmap is sRGB we don't need conversion
         if (colorSpace != nullptr && !colorSpace->isSRGB()) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index eed5b24..729c7b8 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -228,7 +228,7 @@
     bool hasLinearBlending = caches.extensions().hasLinearBlending();
     GLint format, type, internalFormat;
     uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
-            needSRGB, &internalFormat, &format, &type);
+            needSRGB && hasLinearBlending, &internalFormat, &format, &type);
 
     PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
     sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 969d877..ed32832 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -93,6 +93,15 @@
     static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height,
             uirenderer::RenderNode* renderNode = nullptr);
 
+    enum class XformToSRGB {
+        // Transform any Bitmaps to the sRGB color space before drawing.
+        kImmediate,
+
+        // Draw the Bitmap as is.  This likely means that we are recording and that the
+        // transform can be handled at playback time.
+        kDefer,
+    };
+
     /**
      *  Create a new Canvas object which delegates to an SkCanvas.
      *
@@ -100,10 +109,12 @@
      *      delegated to this object. This function will call ref() on the
      *      SkCanvas, and the returned Canvas will unref() it upon
      *      destruction.
+     *  @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
+     *      color space before drawing.
      *  @return new non-null Canvas Object.  The type of DisplayList produced by this canvas is
      *      determined based on  Properties::getRenderPipelineType().
      */
-    static Canvas* create_canvas(SkCanvas* skiaCanvas);
+    static Canvas* create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB);
 
     /**
      *  Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 7fb75dc..44476af 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -44,7 +44,8 @@
         // record the same text draw into a SkPicture and replay it into a Recording canvas
         SkPictureRecorder recorder;
         SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
-        std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
+        std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas,
+                Canvas::XformToSRGB::kDefer));
         TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
         sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
@@ -63,7 +64,7 @@
 
 TEST(SkiaCanvas, drawShadowLayer) {
     auto surface = SkSurface::MakeRasterN32Premul(10, 10);
-    SkiaCanvas canvas(surface->getCanvas());
+    SkiaCanvas canvas(surface->getCanvas(), Canvas::XformToSRGB::kDefer);
 
     // clear to white
     canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
@@ -78,3 +79,52 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
     ASSERT_NE(TestUtils::getColor(surface, 5, 5), SK_ColorWHITE);
 }
+
+TEST(SkiaCanvas, colorSpaceXform) {
+    sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                      SkColorSpace::kAdobeRGB_Gamut);
+
+    SkImageInfo adobeInfo = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType, adobe);
+    sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo);
+    SkBitmap adobeSkBitmap;
+    adobeBitmap->getSkBitmap(&adobeSkBitmap);
+    adobeSkBitmap.lockPixels();
+    *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red
+
+    SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
+    sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+    SkBitmap skBitmap;
+    bitmap->getSkBitmap(&skBitmap);
+
+    // Create a software canvas.
+    SkiaCanvas canvas(skBitmap);
+    canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
+    // The result should be fully red, since we convert to sRGB at draw time.
+    skBitmap.lockPixels();
+    ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
+
+    // Now try in kDefer mode.  This is a little strange given that, in practice, all software
+    // canvases are kImmediate.
+    SkCanvas skCanvas(skBitmap);
+    SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer);
+    deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
+    // The result should be as initialized, since we deferred the conversion to sRGB.
+    skBitmap.lockPixels();
+    ASSERT_EQ(0xFF0000F0, *skBitmap.getAddr32(0, 0));
+
+    // Test picture recording.  We will kDefer the xform at recording time, but handle it when
+    // we playback to the software canvas.
+    SkPictureRecorder recorder;
+    SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
+    SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+    picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
+    sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
+
+    // Playback to a deferred canvas.  The result should be as initialized.
+    deferCanvas.asSkCanvas()->drawPicture(picture);
+    ASSERT_EQ(0xFF0000F0, *skBitmap.getAddr32(0, 0));
+
+    // Playback to an immediate canvas.  The result should be fully red.
+    canvas.asSkCanvas()->drawPicture(picture);
+    ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2723826..b8d1d12 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
@@ -1014,13 +1021,13 @@
      * Set a DRM engine plugin String property value.
      */
     public native void setPropertyString(
-            @StringProperty String propertyName, @NonNull String value);
+            String propertyName, @NonNull String value);
 
     /**
      * Set a DRM engine plugin byte array property value.
      */
     public native void setPropertyByteArray(
-            @ArrayProperty String propertyName, @NonNull byte[] value);
+            String propertyName, @NonNull byte[] value);
 
     private static final native void setCipherAlgorithmNative(
             @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index cd0e587..54442b3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -154,18 +154,18 @@
     @SmallTest
     public void testGetTypeFromTag() {
         assertEquals(TYPE_BYTE,
-                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE, Long.MAX_VALUE));
         assertEquals(TYPE_RATIONAL,
-                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM, Long.MAX_VALUE));
         assertEquals(TYPE_FLOAT,
-                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS));
+                CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_GAINS, Long.MAX_VALUE));
         assertEquals(TYPE_BYTE,
-                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE, Long.MAX_VALUE));
         assertEquals(TYPE_INT32,
-                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
+                CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, Long.MAX_VALUE));
 
         try {
-            CameraMetadataNative.getNativeType(0xDEADF00D);
+            CameraMetadataNative.getNativeType(0xDEADF00D, Long.MAX_VALUE);
             fail("No type should exist for invalid tag 0xDEADF00D");
         } catch(IllegalArgumentException e) {
         }
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/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 77f2e19..a1c8de5 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -122,9 +122,6 @@
                     return true;
                 }
             }
-            for (BluetoothDevice src : srcs) {
-                mService.disconnect(src);
-            }
         }
         return mService.connect(device);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 9b699bc..169aac9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -132,11 +132,6 @@
                     return true;
                 }
             }
-            // Handsfree HF only supports one source connection and hence it is OK to disconnect
-            // the only connected device here.
-            for (BluetoothDevice src : srcs) {
-                mService.disconnect(src);
-            }
         }
         return mService.connect(device);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index a7621fc..6efa468 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -115,10 +115,10 @@
     public boolean connect(BluetoothDevice device) {
         if (mService == null) return false;
         List<BluetoothDevice> connectedDevices = getConnectedDevices();
-        if (connectedDevices != null) {
-            for (BluetoothDevice connectedDevice : connectedDevices) {
-                mService.disconnect(connectedDevice);
-            }
+        if (connectedDevices != null && connectedDevices.contains(device)) {
+            // Connect to same device, Ignore it
+            Log.d(TAG,"Ignoring Connect");
+            return true;
         }
         return mService.connect(device);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 72a3b3a..bd37abe 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -144,9 +144,6 @@
                     return true;
                 }
             }
-            for (BluetoothDevice src : srcs) {
-                mService.disconnect(src);
-            }
         }
         Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
 
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/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 ab7c6d2..a2becf7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTrackerFactory.java
@@ -27,7 +27,7 @@
 
     private static WifiTracker sTestingWifiTracker;
 
-    @Keep
+    @Keep // Keep proguard from stripping this method since it is only used in tests
     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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1147f16..bf39bc4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -183,6 +183,9 @@
     <!-- to control accessibility volume -->
     <uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
 
+    <!-- to access ResolverRankerServices -->
+    <uses-permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 195eb9b..ff22ffb 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -97,6 +97,15 @@
                 android:layout_height="wrap_content"
                 android:text="@string/notification_channel_disabled"
                 style="@style/TextAppearance.NotificationInfo.Secondary" />
+            <!-- Optional link to app. Only appears if the channel is not disabled -->
+            <TextView
+                android:id="@+id/app_settings"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:ellipsize="end"
+                android:maxLines="1"
+                style="@style/TextAppearance.NotificationInfo.Secondary.Link"/>
         </LinearLayout>
         <!-- Ban Channel Switch -->
         <Switch
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a8cf3da..43aeaa3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1472,6 +1472,9 @@
     <!-- Notification: Control panel: Label for button that launches notification settings. Used
         when this app has only defined a single channel for notifications. -->
     <string name="notification_more_settings">More settings</string>
+    <!-- Notification: Control panel: Label for a link that launches notification settings in the
+        app that sent the notification. -->
+    <string name="notification_app_settings">Customize: <xliff:g id="sub_category" example="Work chats">%1$s</xliff:g></string>
     <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
     <string name="notification_done">Done</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d6abda6..c9479b8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -370,6 +370,10 @@
         <item name="android:textColor">?android:attr/colorError</item>
     </style>
 
+    <style name="TextAppearance.NotificationInfo.Secondary.Link">
+        <item name="android:textColor">?android:attr/colorAccent</item>
+    </style>
+
     <style name="TextAppearance.NotificationInfo.Button">
         <item name="android:fontFamily">sans-serif-medium</item>
         <item name="android:textSize">14sp</item>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3a43d30..1c71da0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -355,10 +355,9 @@
                 rti.firstActiveTime = rti.lastActiveTime = i;
                 if (i % 2 == 0) {
                     rti.taskDescription = new ActivityManager.TaskDescription(description,
-                            Bitmap.createBitmap(mDummyIcon), null,
-                            0xFF000000 | (0xFFFFFF & new Random().nextInt()),
-                            0xFF000000 | (0xFFFFFF & new Random().nextInt()),
-                            0, 0);
+                        Bitmap.createBitmap(mDummyIcon), null,
+                        0xFF000000 | (0xFFFFFF & new Random().nextInt()),
+                        0xFF000000 | (0xFFFFFF & new Random().nextInt()));
                 } else {
                     rti.taskDescription = new ActivityManager.TaskDescription();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 8298f35..b4b1cd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -16,45 +16,34 @@
 
 package com.android.systemui.statusbar;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
 import android.app.INotificationManager;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
+import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.widget.CompoundButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.SeekBar;
 import android.widget.Switch;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.lang.IllegalArgumentException;
 import java.util.List;
@@ -71,12 +60,16 @@
     private int mAppUid;
     private List<NotificationChannel> mNotificationChannels;
     private NotificationChannel mSingleNotificationChannel;
+    private StatusBarNotification mSbn;
     private int mStartingUserImportance;
 
     private TextView mNumChannelsView;
     private View mChannelDisabledView;
+    private TextView mSettingsLinkView;
     private Switch mChannelEnabledSwitch;
     private CheckSaveListener mCheckSaveListener;
+    private OnAppSettingsClickListener mAppSettingsClickListener;
+    private PackageManager mPm;
 
     private NotificationGuts mGutsContainer;
 
@@ -95,11 +88,18 @@
         void onClick(View v, NotificationChannel channel, int appUid);
     }
 
+    public interface OnAppSettingsClickListener {
+        void onClick(View v, Intent intent);
+    }
+
     public void bindNotification(final PackageManager pm,
             final INotificationManager iNotificationManager,
             final String pkg,
             final List<NotificationChannel> notificationChannels,
+            int startingUserImportance,
+            final StatusBarNotification sbn,
             OnSettingsClickListener onSettingsClick,
+            OnAppSettingsClickListener onAppSettingsClick,
             OnClickListener onDoneClick,
             CheckSaveListener checkSaveListener,
             final Set<String> nonBlockablePkgs)
@@ -108,16 +108,21 @@
         mPkg = pkg;
         mNotificationChannels = notificationChannels;
         mCheckSaveListener = checkSaveListener;
+        mSbn = sbn;
+        mPm = pm;
+        mAppSettingsClickListener = onAppSettingsClick;
         boolean isSingleDefaultChannel = false;
+        mStartingUserImportance = startingUserImportance;
         if (mNotificationChannels.isEmpty()) {
             throw new IllegalArgumentException("bindNotification requires at least one channel");
-        } else if (mNotificationChannels.size() == 1) {
-            mSingleNotificationChannel = mNotificationChannels.get(0);
-            mStartingUserImportance = mSingleNotificationChannel.getImportance();
-            isSingleDefaultChannel = mSingleNotificationChannel.getId()
-                    .equals(NotificationChannel.DEFAULT_CHANNEL_ID);
-        } else {
-            mSingleNotificationChannel = null;
+        } else  {
+            if (mNotificationChannels.size() == 1) {
+                mSingleNotificationChannel = mNotificationChannels.get(0);
+                isSingleDefaultChannel = mSingleNotificationChannel.getId()
+                        .equals(NotificationChannel.DEFAULT_CHANNEL_ID);
+            } else {
+                mSingleNotificationChannel = null;
+            }
         }
 
         String appName = mPkg;
@@ -248,6 +253,9 @@
         final TextView doneButton = (TextView) findViewById(R.id.done);
         doneButton.setText(R.string.notification_done);
         doneButton.setOnClickListener(onDoneClick);
+
+        // Optional settings link
+        updateAppSettingsLink();
     }
 
     private boolean hasImportanceChanged() {
@@ -276,7 +284,7 @@
 
     private int getSelectedImportance() {
         if (!mChannelEnabledSwitch.isChecked()) {
-            return NotificationManager.IMPORTANCE_NONE;
+            return IMPORTANCE_NONE;
         } else {
             return mStartingUserImportance;
         }
@@ -286,7 +294,7 @@
         // Enabled Switch
         mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
         mChannelEnabledSwitch.setChecked(
-                mStartingUserImportance != NotificationManager.IMPORTANCE_NONE);
+                mStartingUserImportance != IMPORTANCE_NONE);
         final boolean visible = !nonBlockable && mSingleNotificationChannel != null;
         mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
 
@@ -296,12 +304,13 @@
                 mGutsContainer.resetFalsingCheck();
             }
             updateSecondaryText();
+            updateAppSettingsLink();
         });
     }
 
     private void updateSecondaryText() {
         final boolean disabled = mSingleNotificationChannel != null &&
-                getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
+                getSelectedImportance() == IMPORTANCE_NONE;
         if (disabled) {
             mChannelDisabledView.setVisibility(View.VISIBLE);
             mNumChannelsView.setVisibility(View.GONE);
@@ -311,6 +320,45 @@
         }
     }
 
+    private void updateAppSettingsLink() {
+        mSettingsLinkView = findViewById(R.id.app_settings);
+        Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
+                mSbn.getId(), mSbn.getTag());
+        if (settingsIntent != null && getSelectedImportance() != IMPORTANCE_NONE
+                && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+            mSettingsLinkView.setVisibility(View.VISIBLE);
+            mSettingsLinkView.setText(mContext.getString(R.string.notification_app_settings,
+                    mSbn.getNotification().getSettingsText()));
+            mSettingsLinkView.setOnClickListener((View view) -> {
+                mAppSettingsClickListener.onClick(view, settingsIntent);
+            });
+        } else {
+            mSettingsLinkView.setVisibility(View.GONE);
+        }
+    }
+
+    private Intent getAppSettingsIntent(PackageManager pm, String packageName,
+            NotificationChannel channel, int id, String tag) {
+        Intent intent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
+                .setPackage(packageName);
+        final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
+                intent,
+                PackageManager.MATCH_DEFAULT_ONLY
+        );
+        if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
+            return null;
+        }
+        final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
+        intent.setClassName(activityInfo.packageName, activityInfo.name);
+        if (channel != null) {
+            intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
+        }
+        intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
+        intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
+        return intent;
+    }
+
     @Override
     public void setGutsParent(NotificationGuts guts) {
         mGutsContainer = guts;
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 472af65..9304de5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -48,8 +48,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -5701,7 +5703,7 @@
                        .findViewById(com.android.internal.R.id.media_actions) != null;
     }
 
-    // The (i) button in the guts that links to the system notification settings for that app
+    // The button in the guts that links to the system notification settings for that app
     private void startAppNotificationSettingsActivity(String packageName, final int appUid,
             final NotificationChannel channel) {
         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
@@ -5781,10 +5783,17 @@
                     startAppNotificationSettingsActivity(pkg, appUid, channel);
                 };
             }
+            final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
+                    Intent intent) -> {
+                mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS);
+                guts.resetFalsingCheck();
+                startNotificationGutsIntent(intent, sbn.getUid());
+            };
             final View.OnClickListener onDoneClick = (View v) -> {
                 saveAndCloseNotificationMenu(info, row, guts, v);
             };
-            final NotificationInfo.CheckSaveListener checkSaveListener = (Runnable saveImportance) -> {
+            final NotificationInfo.CheckSaveListener checkSaveListener =
+                    (Runnable saveImportance) -> {
                 // If the user has security enabled, show challenge if the setting is changed.
                 if (isLockscreenPublicMode(userHandle.getIdentifier())
                         && (mState == StatusBarState.KEYGUARD
@@ -5817,7 +5826,9 @@
             }
             try {
                 info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
-                        onSettingsClick, onDoneClick, checkSaveListener, mNonBlockablePkgs);
+                        row.getEntry().channel.getImportance(), sbn, onSettingsClick,
+                        onAppSettingsClick, onDoneClick, checkSaveListener,
+                        mNonBlockablePkgs);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
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 0621f4a..2bb7f3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -18,9 +18,9 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -33,13 +33,18 @@
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -56,6 +61,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -74,6 +81,7 @@
     private final PackageManager mMockPackageManager = mock(PackageManager.class);
     private NotificationChannel mNotificationChannel;
     private NotificationChannel mDefaultNotificationChannel;
+    private StatusBarNotification mSbn;
 
     @Before
     public void setUp() throws Exception {
@@ -101,6 +109,8 @@
         mDefaultNotificationChannel = new NotificationChannel(
                 NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
                 NotificationManager.IMPORTANCE_LOW);
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
     }
 
     private CharSequence getStringById(int resId) {
@@ -135,7 +145,9 @@
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
     }
@@ -146,7 +158,9 @@
         when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
                 .thenReturn(iconDrawable);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final ImageView iconView = (ImageView) mNotificationInfo.findViewById(R.id.pkgicon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -154,7 +168,9 @@
     @Test
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.GONE, groupNameView.getVisibility());
         final TextView groupDividerView =
@@ -171,7 +187,9 @@
                 eq("test_group_id"), eq(TEST_PACKAGE_NAME), anyInt()))
                 .thenReturn(notificationChannelGroup);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -183,7 +201,9 @@
     @Test
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -193,10 +213,11 @@
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn,
                 (View v, NotificationChannel c, int appUid) -> {
-                  assertEquals(mNotificationChannel, c);
-                  latch.countDown();
-                }, null, null, null);
+                    assertEquals(mNotificationChannel, c);
+                    latch.countDown();
+                }, null, null, null, null);
 
         final TextView settingsButton =
                 (TextView) mNotificationInfo.findViewById(R.id.more_settings);
@@ -209,7 +230,7 @@
     public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         final TextView settingsButton =
                 (TextView) mNotificationInfo.findViewById(R.id.more_settings);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -219,10 +240,11 @@
     public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
-                (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+                mNotificationChannel.getImportance(), mSbn,
+                (View v, NotificationChannel c, int appUid) -> {}, null, null, null, null);
         final TextView settingsButton =
                 (TextView) mNotificationInfo.findViewById(R.id.more_settings);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
@@ -234,10 +256,11 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME,
                 Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn,
                 (View v, NotificationChannel c, int appUid) -> {
-                  assertEquals(null, c);
-                  latch.countDown();
-                }, null, null, null);
+                    assertEquals(null, c);
+                    latch.countDown();
+                }, null, null, null, null);
 
         final TextView settingsButton =
                 (TextView) mNotificationInfo.findViewById(R.id.more_settings);
@@ -250,7 +273,9 @@
     public void testBindNotification_SettingsTextWithOneChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
-                (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+                mNotificationChannel.getImportance(), mSbn,
+                (View v, NotificationChannel c, int appUid) -> {
+                }, null, null, null, null);
         final TextView settingsButton =
                 (TextView) mNotificationInfo.findViewById(R.id.more_settings);
         assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText());
@@ -262,7 +287,9 @@
                 eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
-                (View v, NotificationChannel c, int appUid) -> {}, null, null, null);
+                mNotificationChannel.getImportance(), mSbn,
+                (View v, NotificationChannel c, int appUid) -> {
+                }, null, null, null, null);
         final TextView settingsButton =
                 (TextView) mNotificationInfo.findViewById(R.id.more_settings);
         assertEquals(getStringById(R.string.notification_all_categories), settingsButton.getText());
@@ -272,8 +299,11 @@
     public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null,
-                (View v) -> { latch.countDown(); },
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null,
+                null, (View v) -> {
+                    latch.countDown();
+                },
                 null, null);
 
         final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done);
@@ -286,7 +316,8 @@
     public void testBindNotification_NumChannelsTextUniqueWhenDefaultChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null,
+                null, null, null);
         final TextView numChannelsView =
                 (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
         assertEquals(View.VISIBLE, numChannelsView.getVisibility());
@@ -298,7 +329,9 @@
     public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel()
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView numChannelsView =
                 (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
         assertEquals(numChannelsView.getVisibility(), View.VISIBLE);
@@ -311,7 +344,9 @@
         when(mMockINotificationManager.getNumNotificationChannelsForPackage(
                 eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView numChannelsView =
                 (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
         assertEquals(getNumChannelsDescString(2), numChannelsView.getText());
@@ -323,7 +358,7 @@
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         final TextView numChannelsView =
                 (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
         assertEquals(getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel),
@@ -339,7 +374,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME,
                 Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         final TextView numChannelsView =
                 (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
         assertEquals(
@@ -359,8 +394,8 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME,
                 Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel,
-                        fourthChannel),
-                null, null, null, null);
+                        fourthChannel), mNotificationChannel.getImportance(), mSbn, null, null,
+                null, null, null);
         final TextView numChannelsView =
                 (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
         assertEquals(
@@ -375,7 +410,7 @@
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         final TextView channelNameView =
                 (TextView) mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(getNumChannelsString(2), channelNameView.getText());
@@ -386,7 +421,7 @@
     public void testEnabledSwitchInvisibleIfBundleFromDifferentChannels() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
-                null, null, null, null);
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
     }
@@ -394,7 +429,8 @@
     @Test
     public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null, null, null);
         final TextView channelDisabledView =
                 (TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
         assertEquals(channelDisabledView.getVisibility(), View.GONE);
@@ -404,7 +440,9 @@
     public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         final TextView channelDisabledView =
                 (TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
         assertEquals(channelDisabledView.getVisibility(), View.VISIBLE);
@@ -421,7 +459,8 @@
         mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel),
-                null, null, null, null);
+                mDefaultNotificationChannel.getImportance(), mSbn, null, null,
+                null, null, null);
         final TextView channelDisabledView =
                 (TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
         assertEquals(View.VISIBLE, channelDisabledView.getVisibility());
@@ -430,7 +469,9 @@
     @Test
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
     }
@@ -439,7 +480,9 @@
     public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         enabledSwitch.setChecked(false);
@@ -451,7 +494,9 @@
     public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
 
         mNotificationInfo.handleCloseControls(true);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -463,7 +508,9 @@
             throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
 
         mNotificationInfo.handleCloseControls(true);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -474,7 +521,9 @@
     public void testEnabledSwitchOnByDefault() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertTrue(enabledSwitch.isChecked());
@@ -484,7 +533,9 @@
     public void testEnabledButtonOffWhenAlreadyBanned() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertFalse(enabledSwitch.isChecked());
@@ -494,7 +545,9 @@
     public void testEnabledSwitchVisibleByDefault() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null, null);
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, null);
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
@@ -504,8 +557,9 @@
     public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
@@ -515,8 +569,9 @@
     public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, Collections.singleton(TEST_PACKAGE_NAME));
         mNotificationInfo.handleCloseControls(true);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
@@ -526,8 +581,9 @@
     public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         enabledSwitch.setChecked(false);
@@ -540,8 +596,9 @@
     public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                null, Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         enabledSwitch.setChecked(false);
@@ -554,8 +611,10 @@
     public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null,
-                (Runnable saveImportance) -> {},
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                (Runnable saveImportance) -> {
+                },
                 Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
@@ -569,8 +628,11 @@
     public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), null, null,
-                (Runnable saveImportance) -> { saveImportance.run(); },
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), mSbn, null, null, null,
+                (Runnable saveImportance) -> {
+                    saveImportance.run();
+                },
                 Collections.singleton(TEST_PACKAGE_NAME));
 
         Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
@@ -579,4 +641,137 @@
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
     }
+
+    @Test
+    public void testDisplaySettingsLink() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), sbn, null,
+                (View v, Intent intent) -> {
+                    latch.countDown();
+                }, null, null, null);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.VISIBLE, settingsLink.getVisibility());
+        assertTrue(settingsLink.getText().toString().length() > settingsText.length());
+        assertTrue(settingsLink.getText().toString().contains(settingsText));
+        settingsLink.performClick();
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testDisplaySettingsLink_multipleChannels() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel),
+                mNotificationChannel.getImportance(), sbn, null, (View v, Intent intent) -> {
+                    latch.countDown();
+                }, null, null, null);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.VISIBLE, settingsLink.getVisibility());
+        settingsLink.performClick();
+        assertEquals(0, latch.getCount());
+    }
+
+    @Test
+    public void testNoSettingsLink_noHandlingActivity() throws Exception {
+        final String settingsText = "work chats";
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
+        mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), sbn, null, null, null,
+                null, null);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.GONE, settingsLink.getVisibility());
+    }
+
+    @Test
+    public void testNoSettingsLink_noLinkText() throws Exception {
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), sbn, null, null, null,
+                null, null);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.GONE, settingsLink.getVisibility());
+    }
+
+    @Test
+    public void testNoSettingsLink_afterBlockingChannel() throws Exception {
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
+                mNotificationChannel.getImportance(), sbn, null, null, null,
+                null, null);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(View.VISIBLE, settingsLink.getVisibility());
+
+        // Block channel
+        Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
+        enabledSwitch.setChecked(false);
+
+        assertEquals(View.GONE, settingsLink.getVisibility());
+
+        //unblock
+        enabledSwitch.setChecked(true);
+        assertEquals(View.VISIBLE, settingsLink.getVisibility());
+    }
 }
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
index a69de7a..2c49f99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,6 +38,7 @@
         mContext = InstrumentationRegistry.getTargetContext();
     }
 
+    @Ignore("Broken")
     @Test
     public void constructor_doesntUseViewContext() throws Exception {
         new TestableNotificationViewWrapper(mContext, new View(null /* context */), null /* row */);
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..906f81c 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;
@@ -56,6 +57,7 @@
 import android.view.autofill.IAutoFillManagerClient;
 import android.view.autofill.IAutofillWindowPresenter;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -100,7 +102,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 +134,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 +143,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;
@@ -181,6 +191,9 @@
     public void onFillRequestSuccess(@Nullable FillResponse response,
             @NonNull String servicePackageName) {
         if (response == null) {
+            if ((mFlags & FLAG_MANUAL_REQUEST) != 0) {
+                getUiForShowing().showError(R.string.autofill_error_cannot_autofill);
+            }
             // Nothing to be done, but need to notify client.
             notifyUnavailableToClient();
             removeSelf();
@@ -302,12 +315,22 @@
     @Override
     public void requestShowFillUi(AutofillId id, int width, int height,
             IAutofillWindowPresenter presenter) {
-        try {
-            final ViewState currentView = mViewStates.get(mCurrentViewId);
-            mClient.requestShowFillUi(mWindowToken, id, width, height,
-                    currentView.getVirtualBounds(), presenter);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Error requesting to show fill UI", e);
+        synchronized (mLock) {
+            if (id.equals(mCurrentViewId)) {
+                try {
+                    final ViewState view = mViewStates.get(id);
+                    mClient.requestShowFillUi(mWindowToken, id, width, height,
+                            view.getVirtualBounds(),
+                            presenter);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error requesting to show fill UI", e);
+                }
+            } else {
+                if (DEBUG) {
+                    Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view ("
+                            + mCurrentViewId + ") anymore");
+                }
+            }
         }
     }
 
@@ -352,6 +375,10 @@
         mHasCallback = hasIt;
     }
 
+    public void setStructureLocked(AssistStructure structure) {
+        mStructure = structure;
+    }
+
     /**
      * Shows the save UI, when session can be saved.
      *
@@ -474,9 +501,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 +530,7 @@
             mStructure.dump();
         }
 
-        mRemoteFillService.onSaveRequest(mStructure, extras);
+        mRemoteFillService.onSaveRequest(mStructure, mExtras);
     }
 
     void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
@@ -517,10 +541,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 +608,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 +687,9 @@
             mResponses = new ArrayList<>(4);
         }
         mResponses.add(response);
+        if (response != null) {
+            mExtras = response.getExtras();
+        }
 
         setViewStatesLocked(response, ViewState.STATE_FILLABLE);
 
@@ -695,7 +735,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 +831,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/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 832ff9a..257256d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -89,6 +89,13 @@
     /**
      * Displays an error message to the user.
      */
+    public void showError(int resId) {
+        showError(mContext.getString(resId));
+    }
+
+    /**
+     * Displays an error message to the user.
+     */
     public void showError(@Nullable CharSequence message) {
         mHandler.post(() -> {
             if (!hasCallback()) {
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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 25ac008..50c0a12 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1265,13 +1265,16 @@
     @Override
     public LinkProperties getLinkProperties(Network network) {
         enforceAccessPermission();
-        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-        if (nai != null) {
-            synchronized (nai) {
-                return new LinkProperties(nai.linkProperties);
-            }
+        return getLinkProperties(getNetworkAgentInfoForNetwork(network));
+    }
+
+    private LinkProperties getLinkProperties(NetworkAgentInfo nai) {
+        if (nai == null) {
+            return null;
         }
-        return null;
+        synchronized (nai) {
+            return new LinkProperties(nai.linkProperties);
+        }
     }
 
     private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
@@ -3027,7 +3030,8 @@
         enforceAccessPermission();
         enforceInternetPermission();
 
-        NetworkAgentInfo nai;
+        // TODO: execute this logic on ConnectivityService handler.
+        final NetworkAgentInfo nai;
         if (network == null) {
             nai = getDefaultNetwork();
         } else {
@@ -3038,21 +3042,24 @@
             return;
         }
         // Revalidate if the app report does not match our current validated state.
-        if (hasConnectivity == nai.lastValidated) return;
+        if (hasConnectivity == nai.lastValidated) {
+            return;
+        }
         final int uid = Binder.getCallingUid();
         if (DBG) {
             log("reportNetworkConnectivity(" + nai.network.netId + ", " + hasConnectivity +
                     ") by " + uid);
         }
-        synchronized (nai) {
-            // Validating a network that has not yet connected could result in a call to
-            // rematchNetworkAndRequests() which is not meant to work on such networks.
-            if (!nai.everConnected) return;
-
-            if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) return;
-
-            nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
+        // Validating a network that has not yet connected could result in a call to
+        // rematchNetworkAndRequests() which is not meant to work on such networks.
+        if (!nai.everConnected) {
+            return;
         }
+        LinkProperties lp = getLinkProperties(nai);
+        if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
+            return;
+        }
+        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
     }
 
     private ProxyInfo getDefaultProxy() {
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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ee2fdba..73aeee9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -333,7 +333,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.service.voice.VoiceInteractionSession;
-import android.service.vr.IPersistentVrStateCallbacks;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -664,70 +663,8 @@
     // default action automatically.  Important for devices without direct input
     // devices.
     private boolean mShowDialogs = true;
-    // VR state flags.
-    static final int NON_VR_MODE = 0;
-    static final int VR_MODE = 1;
-    static final int PERSISTENT_VR_MODE = 2;
-    private int mVrState = NON_VR_MODE;
-    private int mTopAppVrThreadTid = 0;
-    private int mPersistentVrThreadTid = 0;
-    final IPersistentVrStateCallbacks mPersistentVrModeListener =
-            new IPersistentVrStateCallbacks.Stub() {
-        @Override
-        public void onPersistentVrStateChanged(boolean enabled) {
-            synchronized(ActivityManagerService.this) {
-                // There are 4 possible cases here:
-                //
-                // Cases for enabled == true
-                // Invariant: mVrState != PERSISTENT_VR_MODE;
-                //    This is guaranteed as only this function sets mVrState to PERSISTENT_VR_MODE
-                // Invariant: mPersistentVrThreadTid == 0
-                //   This is guaranteed by VrManagerService, which only emits callbacks when the
-                //   mode changes, and in setPersistentVrThread, which only sets
-                //   mPersistentVrThreadTid when mVrState = PERSISTENT_VR_MODE
-                // Case 1: mTopAppVrThreadTid > 0 (someone called setVrThread successfully and is
-                // the top-app)
-                //     We reset the app which currently has SCHED_FIFO (mPersistentVrThreadTid) to
-                //     SCHED_OTHER
-                // Case 2: mTopAppVrThreadTid == 0
-                //     Do nothing
-                //
-                // Cases for enabled == false
-                // Invariant: mVrState == PERSISTENT_VR_MODE;
-                //     This is guaranteed by VrManagerService, which only emits callbacks when the
-                //     mode changes, and the only other assignment of mVrState outside of this
-                //     function checks if mVrState != PERSISTENT_VR_MODE
-                // Invariant: mTopAppVrThreadTid == 0
-                //     This is guaranteed in that mTopAppVrThreadTid is only set to a tid when
-                //     mVrState is VR_MODE, and is explicitly set to 0 when setPersistentVrThread is
-                //     called
-                //   mPersistentVrThreadTid > 0 (someone called setPersistentVrThread successfully)
-                //     3. Reset mPersistentVrThreadTidto SCHED_OTHER
-                //   mPersistentVrThreadTid == 0
-                //     4. Do nothing
-                if (enabled) {
-                    mVrState = PERSISTENT_VR_MODE;
-                } else {
-                    // Leaving persistent mode implies leaving VR mode.
-                    mVrState = NON_VR_MODE;
-                }
 
-                if (mVrState == PERSISTENT_VR_MODE) {
-                    if (mTopAppVrThreadTid > 0) {
-                        // Ensure that when entering persistent VR mode the last top-app loses
-                        // SCHED_FIFO.
-                        setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0);
-                        mTopAppVrThreadTid = 0;
-                    }
-                } else if (mPersistentVrThreadTid > 0) {
-                    // Ensure that when leaving persistent VR mode we reschedule the high priority
-                    // persistent thread.
-                    setThreadScheduler(mPersistentVrThreadTid, SCHED_OTHER, 0);
-                    mPersistentVrThreadTid = 0;
-                }
-            }
-        }
-    };
+    private final VrController mVrController;
 
     // VR Compatibility Display Id.
     int mVrCompatibilityDisplayId = INVALID_DISPLAY;
@@ -2447,53 +2384,19 @@
                 idleUids();
             } break;
             case VR_MODE_CHANGE_MSG: {
-                VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-                if (vrService == null) {
-                    break;
-                }
-                final ActivityRecord r = (ActivityRecord) msg.obj;
-                boolean vrMode;
-                boolean inVrMode;
-                ComponentName requestedPackage;
-                ComponentName callingPackage;
-                int userId;
-                synchronized (ActivityManagerService.this) {
-                    vrMode = r.requestedVrComponent != null;
-                    inVrMode = mVrState != NON_VR_MODE;
-                    requestedPackage = r.requestedVrComponent;
-                    userId = r.userId;
-                    callingPackage = r.info.getComponentName();
-                    if (vrMode != inVrMode) {
-                        // Don't change state if we're in persistent VR mode, but do update thread
-                        // priorities if necessary.
-                        if (mVrState != PERSISTENT_VR_MODE) {
-                            mVrState = vrMode ? VR_MODE : NON_VR_MODE;
-                        }
-                        mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), vrMode);
-                        if (r.app != null) {
-                            ProcessRecord proc = r.app;
-                            if (proc.vrThreadTid > 0) {
-                                if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
-                                    try {
-                                        if (mVrState == VR_MODE) {
-                                            setThreadScheduler(proc.vrThreadTid,
-                                                SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
-                                            mTopAppVrThreadTid = proc.vrThreadTid;
-                                        } else {
-                                            setThreadScheduler(proc.vrThreadTid,
-                                                SCHED_OTHER, 0);
-                                            mTopAppVrThreadTid = 0;
-                                        }
-                                    } catch (IllegalArgumentException e) {
-                                        Slog.w(TAG, "Failed to set scheduling policy, thread does"
-                                                + " not exist:\n" + e);
-                                    }
-                                }
+                if (mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
+                    synchronized (ActivityManagerService.this) {
+                        if (mVrController.shouldDisableNonVrUiLocked()) {
+                            // If we are in a VR mode where Picture-in-Picture mode is unsupported,
+                            // then remove the pinned stack.
+                            final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
+                                    PINNED_STACK_ID);
+                            if (pinnedStack != null) {
+                                mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
                             }
                         }
                     }
                 }
-                vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
             } break;
             case NOTIFY_VR_SLEEPING_MSG: {
                 notifyVrManagerOfSleepState(msg.arg1 != 0);
@@ -2770,6 +2673,7 @@
         mTaskChangeNotificationController = null;
         mUiHandler = injector.getUiHandler(null);
         mUserController = null;
+        mVrController = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2856,6 +2760,8 @@
 
         mUserController = new UserController(this);
 
+        mVrController = new VrController(this);
+
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
@@ -13263,23 +13169,12 @@
 
     @Override
     public void setVrThread(int tid) {
-        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
-            throw new UnsupportedOperationException("VR mode not supported on this device!");
-        }
-
+        enforceSystemHasVrFeature();
         synchronized (this) {
-            if (tid > 0 && mVrState == PERSISTENT_VR_MODE) {
-                Slog.e(TAG, "VR thread cannot be set in persistent VR mode!");
-                return;
-            }
-            ProcessRecord proc;
             synchronized (mPidsSelfLocked) {
                 final int pid = Binder.getCallingPid();
-                proc = mPidsSelfLocked.get(pid);
-                if (proc != null && mVrState == VR_MODE && tid >= 0) {
-                    proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid);
-                    mTopAppVrThreadTid = proc.vrThreadTid;
-                }
+                final ProcessRecord proc = mPidsSelfLocked.get(pid);
+                mVrController.setVrThreadLocked(tid, pid, proc);
             }
         }
     }
@@ -13287,72 +13182,71 @@
     @Override
     public void setPersistentVrThread(int tid) {
         if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
-            String msg = "Permission Denial: setPersistentVrThread() from pid="
+            final String msg = "Permission Denial: setPersistentVrThread() from pid="
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
                     + " requires " + permission.RESTRICTED_VR_ACCESS;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
-            throw new UnsupportedOperationException("VR mode not supported on this device!");
-        }
-
+        enforceSystemHasVrFeature();
         synchronized (this) {
-            // Disable any existing VR thread.
-            if (mTopAppVrThreadTid > 0) {
-                setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0);
-                mTopAppVrThreadTid = 0;
-            }
-
-            if (tid > 0 && mVrState != PERSISTENT_VR_MODE) {
-                Slog.e(TAG, "Persistent VR thread may only be set in persistent VR mode!");
-                return;
-            }
-            ProcessRecord proc;
             synchronized (mPidsSelfLocked) {
                 final int pid = Binder.getCallingPid();
-                mPersistentVrThreadTid =
-                        updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid);
+                final ProcessRecord proc = mPidsSelfLocked.get(pid);
+                mVrController.setPersistentVrThreadLocked(tid, pid, proc);
             }
         }
     }
 
     /**
-     * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is
-     * non-null it must be in SCHED_GROUP_TOP_APP.  When it is null, the tid is unconditionally
-     * rescheduled.
+     * Schedule the given thread a normal scheduling priority.
+     *
+     * @param newTid the tid of the thread to adjust the scheduling of.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return {@code true} if this succeeded.
      */
-    private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) {
-        // ensure the tid belongs to the process
-        if (!isThreadInProcess(pid, tid)) {
-            throw new IllegalArgumentException("VR thread does not belong to process");
-        }
-
-        // reset existing VR thread to CFS if this thread still exists and belongs to
-        // the calling process
-        if (lastTid != 0 && isThreadInProcess(pid, lastTid)) {
-            try {
-                setThreadScheduler(lastTid, SCHED_OTHER, 0);
-            } catch (IllegalArgumentException e) {
-                // Ignore this.  Only occurs in race condition where previous VR thread
-                // was destroyed during this method call.
-            }
-        }
-
-        // promote to FIFO now if the tid is non-zero
+    static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) {
         try {
-            if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP)
-                    && tid > 0) {
-                setThreadScheduler(tid,
-                    SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
-            }
-            return tid;
+            Process.setThreadScheduler(tid, Process.SCHED_OTHER, 0);
+            return true;
         } catch (IllegalArgumentException e) {
-            Slog.e(TAG, "Failed to set scheduling policy, thread does"
-                   + " not exist:\n" + e);
+            if (!suppressLogs) {
+                Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
+            }
         }
-        return lastTid;
+        return false;
+    }
+
+    /**
+     * Schedule the given thread an FIFO scheduling priority.
+     *
+     * @param newTid the tid of the thread to adjust the scheduling of.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return {@code true} if this succeeded.
+     */
+    static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
+        try {
+            Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+            return true;
+        } catch (IllegalArgumentException e) {
+            if (!suppressLogs) {
+                Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check that we have the features required for VR-related API calls, and throw an exception if
+     * not.
+     */
+    private void enforceSystemHasVrFeature() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+            throw new UnsupportedOperationException("VR mode not supported on this device!");
+        }
     }
 
     @Override
@@ -13447,6 +13341,13 @@
         }
     }
 
+    /**
+     * @return whether the system should disable UI modes incompatible with VR mode.
+     */
+    boolean shouldDisableNonVrUiLocked() {
+        return mVrController.shouldDisableNonVrUiLocked();
+    }
+
     @Override
     public boolean isTopOfTask(IBinder token) {
         synchronized (this) {
@@ -13944,10 +13845,7 @@
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
             mAssistUtils = new AssistUtils(mContext);
-            VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
-            if (vrManagerInternal != null) {
-                vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
-            }
+            mVrController.onSystemReady();
             // Make sure we have the current profile info, since it is needed for security checks.
             mUserController.onSystemReady();
             mRecentTasks.onSystemReadyLocked();
@@ -15629,6 +15527,7 @@
                 pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
             }
         }
+        pw.println("  mVrController=" + mVrController);
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
             if (dumpPackage == null || dumpPackage.equals(mDebugApp)
@@ -20055,7 +19954,7 @@
                 mUserController.getCurrentUserIdLocked());
 
         // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
-        mShowDialogs = shouldShowDialogs(mTempConfig, mVrState != NON_VR_MODE);
+        mShowDialogs = shouldShowDialogs(mTempConfig);
 
         AttributeCache ac = AttributeCache.instance();
         if (ac != null) {
@@ -20285,15 +20184,16 @@
      * A thought: SystemUI might also want to get told about this, the Power
      * dialog / global actions also might want different behaviors.
      */
-    private static boolean shouldShowDialogs(Configuration config, boolean inVrMode) {
+    private static boolean shouldShowDialogs(Configuration config) {
         final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
                                    && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                                    && config.navigation == Configuration.NAVIGATION_NONAV);
         int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
         final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
                 && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE))
-                && modeType != Configuration.UI_MODE_TYPE_TELEVISION);
-        return inputMethodExists && uiModeSupportsDialogs && !inVrMode;
+                && modeType != Configuration.UI_MODE_TYPE_TELEVISION
+                && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
+        return inputMethodExists && uiModeSupportsDialogs;
     }
 
     @Override
@@ -21596,32 +21496,14 @@
                     if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                         // do nothing if we already switched to RT
                         if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                            // Switch VR thread for app to SCHED_FIFO
-                            if (mVrState == VR_MODE && app.vrThreadTid != 0) {
-                                try {
-                                    setThreadScheduler(app.vrThreadTid,
-                                        SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
-                                    mTopAppVrThreadTid = app.vrThreadTid;
-                                } catch (IllegalArgumentException e) {
-                                    // thread died, ignore
-                                }
-                            }
+                            mVrController.onTopProcChangedLocked(app);
                             if (mUseFifoUiScheduling) {
                                 // Switch UI pipeline for app to SCHED_FIFO
-                                app.savedPriority = getThreadPriority(app.pid);
-                                try {
-                                    setThreadScheduler(app.pid,
-                                        SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
-                                } catch (IllegalArgumentException e) {
-                                    // thread died, ignore
-                                }
+                                app.savedPriority = Process.getThreadPriority(app.pid);
+                                scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
                                 if (app.renderThreadTid != 0) {
-                                    try {
-                                        setThreadScheduler(app.renderThreadTid,
-                                            SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
-                                    } catch (IllegalArgumentException e) {
-                                        // thread died, ignore
-                                    }
+                                    scheduleAsFifoPriority(app.renderThreadTid,
+                                        /* suppressLogs */true);
                                     if (DEBUG_OOM_ADJ) {
                                         Slog.d("UI_FIFO", "Set RenderThread (TID " +
                                             app.renderThreadTid + ") to FIFO");
@@ -21645,12 +21527,7 @@
                         }
                     } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
                                app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                        // Reset VR thread to SCHED_OTHER
-                        // Safe to do even if we're not in VR mode
-                        if (app.vrThreadTid != 0) {
-                            setThreadScheduler(app.vrThreadTid, SCHED_OTHER, 0);
-                            mTopAppVrThreadTid = 0;
-                        }
+                        mVrController.onTopProcChangedLocked(app);
                         if (mUseFifoUiScheduling) {
                             // Reset UI pipeline to SCHED_OTHER
                             setThreadScheduler(app.pid, SCHED_OTHER, 0);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 3a29414..6e7ad17 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1164,6 +1164,11 @@
             return false;
         }
 
+        // Check to see if we are in VR mode, and disallow PiP if so
+        if (service.shouldDisableNonVrUiLocked()) {
+            return false;
+        }
+
         boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
         boolean isKeyguardLocked = service.isKeyguardLocked();
         boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
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/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 73a17c6..160c753 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -21,6 +21,9 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -34,11 +37,14 @@
     private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
 
     // mapping form property name to its type
-    private static final Map<String, Class<?>> sSecureSettingToTypeMap = new HashMap<
+    @VisibleForTesting
+    static final Map<String, Class<?>> sSecureSettingToTypeMap = new HashMap<
             String, Class<?>>();
-    private static final Map<String, Class<?>> sSystemSettingToTypeMap = new HashMap<
+    @VisibleForTesting
+    static final Map<String, Class<?>> sSystemSettingToTypeMap = new HashMap<
             String, Class<?>>();
-    private static final Map<String, Class<?>> sGlobalSettingToTypeMap = new HashMap<
+    @VisibleForTesting
+    static final Map<String, Class<?>> sGlobalSettingToTypeMap = new HashMap<
             String, Class<?>>();
     static {
         sSecureSettingToTypeMap.put(Settings.Secure.LONG_PRESS_TIMEOUT, int.class);
@@ -101,51 +107,31 @@
         }
     }
 
-    private void populateSettings(Bundle snapshot, Map<String, Class<?>> map) {
+    @VisibleForTesting
+    void populateSettings(Bundle snapshot, Map<String, Class<?>> map) {
         Context context = mActivityManagerService.mContext;
         for (Map.Entry<String, Class<?>> entry : map.entrySet()) {
             String setting = entry.getKey();
+            final String value;
+            if (map == sSecureSettingToTypeMap) {
+                value = Settings.Secure.getString(context.getContentResolver(), setting);
+            } else if (map == sSystemSettingToTypeMap) {
+                value = Settings.System.getString(context.getContentResolver(), setting);
+            } else {
+                value = Settings.Global.getString(context.getContentResolver(), setting);
+            }
+            if (value == null) {
+                continue;
+            }
             Class<?> type = entry.getValue();
             if (type == String.class) {
-                final String value;
-                if (map == sSecureSettingToTypeMap) {
-                    value = Settings.Secure.getString(context.getContentResolver(), setting);
-                } else if (map == sSystemSettingToTypeMap) {
-                    value = Settings.System.getString(context.getContentResolver(), setting);
-                } else {
-                    value = Settings.Global.getString(context.getContentResolver(), setting);
-                }
                 snapshot.putString(setting, value);
             } else if (type == int.class) {
-                final int value;
-                if (map == sSecureSettingToTypeMap) {
-                    value = Settings.Secure.getInt(context.getContentResolver(), setting, 0);
-                } else if (map == sSystemSettingToTypeMap) {
-                    value = Settings.System.getInt(context.getContentResolver(), setting, 0);
-                } else {
-                    value = Settings.Global.getInt(context.getContentResolver(), setting, 0);
-                }
-                snapshot.putInt(setting, value);
+                snapshot.putInt(setting, Integer.parseInt(value));
             } else if (type == float.class) {
-                final float value;
-                if (map == sSecureSettingToTypeMap) {
-                    value = Settings.Secure.getFloat(context.getContentResolver(), setting, 0);
-                } else if (map == sSystemSettingToTypeMap) {
-                    value = Settings.System.getFloat(context.getContentResolver(), setting, 0);
-                } else {
-                    value = Settings.Global.getFloat(context.getContentResolver(), setting, 0);
-                }
-                snapshot.putFloat(setting, value);
+                snapshot.putFloat(setting, Float.parseFloat(value));
             } else if (type == long.class) {
-                final long value;
-                if (map == sSecureSettingToTypeMap) {
-                    value = Settings.Secure.getLong(context.getContentResolver(), setting, 0);
-                } else if (map == sSystemSettingToTypeMap) {
-                    value = Settings.System.getLong(context.getContentResolver(), setting, 0);
-                } else {
-                    value = Settings.Global.getLong(context.getContentResolver(), setting, 0);
-                }
-                snapshot.putLong(setting, value);
+                snapshot.putLong(setting, Long.parseLong(value));
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c7f20b9f..0c2c204 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1613,9 +1613,6 @@
             String iconFilename = null;
             int colorPrimary = 0;
             int colorBackground = 0;
-            int statusBarColor = 0;
-            int navigationBarColor = 0;
-            boolean topActivity = true;
             for (--activityNdx; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = mActivities.get(activityNdx);
                 if (r.taskDescription != null) {
@@ -1628,16 +1625,13 @@
                     if (colorPrimary == 0) {
                         colorPrimary = r.taskDescription.getPrimaryColor();
                     }
-                    if (topActivity) {
+                    if (colorBackground == 0) {
                         colorBackground = r.taskDescription.getBackgroundColor();
-                        statusBarColor = r.taskDescription.getStatusBarColor();
-                        navigationBarColor = r.taskDescription.getNavigationBarColor();
                     }
                 }
-                topActivity = false;
             }
             lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
-                    colorBackground, statusBarColor, navigationBarColor);
+                    colorBackground);
             if (mWindowContainerController != null) {
                 mWindowContainerController.setTaskDescription(lastTaskDescription);
             }
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/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
new file mode 100644
index 0000000..048bef7
--- /dev/null
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -0,0 +1,418 @@
+/*
+ * 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 android.content.ComponentName;
+import android.os.Process;
+import android.service.vr.IPersistentVrStateCallbacks;
+import android.util.Slog;
+import com.android.server.LocalServices;
+import com.android.server.vr.VrManagerInternal;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager
+ * functionality.
+ *
+ * <p>Specifically, this class is responsible for:
+ * <ul>
+ * <li>Adjusting the scheduling of VR render threads while in VR mode.
+ * <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread.
+ * <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags.
+ * </ul>
+ *
+ * <p>This is NOT the class that manages the system VR mode lifecycle. The class responsible for
+ * handling everything related to VR mode state changes (e.g. the lifecycles of the associated
+ * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService.
+ *
+ * <p>This class is exclusively for use by ActivityManagerService. Do not add callbacks or other
+ * functionality to this for things that belong in VrManagerService.
+ */
+final class VrController {
+    private static final String TAG = "VrController";
+
+    // VR state flags.
+    private static final int FLAG_NON_VR_MODE = 0;
+    private static final int FLAG_VR_MODE = 1;
+    private static final int FLAG_PERSISTENT_VR_MODE = 2;
+
+    // Invariants maintained for mVrState
+    //
+    //   Always true:
+    //      - Only a single VR-related thread will have elevated scheduling priorities at a time
+    //        across all threads in all processes (and for all possible running modes).
+    //
+    //   Always true while FLAG_PERSISTENT_VR_MODE is set:
+    //      - An application has set a flag to run in persistent VR mode the next time VR mode is
+    //        entered. The device may or may not be in VR mode.
+    //      - mVrState will contain FLAG_PERSISTENT_VR_MODE
+    //      - An application may set a persistent VR thread that gains elevated scheduling
+    //        priorities via a call to setPersistentVrThread.
+    //      - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and
+    //        thread that had previously elevated its scheduling priority in this way is returned
+    //        to its normal scheduling priority.
+    //
+    //   Always true while FLAG_VR_MODE is set:
+    //      - The current top application is running in VR mode.
+    //      - mVrState will contain FLAG_VR_MODE
+    //
+    //   While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE:
+    //      - The current top application may set one of its threads to run at an elevated
+    //        scheduling priority via a call to setVrThread.
+    //
+    //   While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE:
+    //      - The current top application may NOT set one of its threads to run at an elevated
+    //        scheduling priority via a call to setVrThread (instead, the persistent VR thread will
+    //        be kept if an application has set one).
+    //
+    //   While mVrState == FLAG_NON_VR_MODE:
+    //      - Calls to setVrThread will fail.
+    //      - Calls to setPersistentVrThread will fail.
+    //      - No threads will have elevated scheduling priority for VR.
+    //
+    private int mVrState = FLAG_NON_VR_MODE;
+
+    // The single VR render thread on the device that is given elevated scheduling priority.
+    private int mVrRenderThreadTid = 0;
+
+    private final Object mGlobalAmLock;
+
+    private final IPersistentVrStateCallbacks mPersistentVrModeListener =
+            new IPersistentVrStateCallbacks.Stub() {
+        @Override
+        public void onPersistentVrStateChanged(boolean enabled) {
+            synchronized(mGlobalAmLock) {
+                // Note: This is the only place where mVrState should have its
+                // FLAG_PERSISTENT_VR_MODE setting changed.
+                if (enabled) {
+                    setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true);
+                    mVrState |= FLAG_PERSISTENT_VR_MODE;
+                } else {
+                    setPersistentVrRenderThreadLocked(0, true);
+                    mVrState &= ~FLAG_PERSISTENT_VR_MODE;
+                }
+            }
+        }
+    };
+
+    /**
+     * Create new VrController instance.
+     *
+     * @param globalAmLock the global ActivityManagerService lock.
+     */
+    public VrController(final Object globalAmLock) {
+        mGlobalAmLock = globalAmLock;
+    }
+
+    /**
+     * Called when ActivityManagerService receives its systemReady call during boot.
+     */
+    public void onSystemReady() {
+        VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
+        if (vrManagerInternal != null) {
+            vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
+        }
+    }
+
+    /**
+     * Called when ActivityManagerService's TOP_APP process has changed.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock held.
+     *
+     * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling
+     *        group.
+     */
+    public void onTopProcChangedLocked(ProcessRecord proc) {
+        if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+            setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true);
+        } else {
+            if (proc.vrThreadTid == mVrRenderThreadTid) {
+                clearVrRenderThreadLocked(true);
+            }
+        }
+    }
+
+    /**
+     * Called when ActivityManagerService is switching VR mode for the TOP_APP process.
+     *
+     * @param record the ActivityRecord of the activity changing the system VR mode.
+     * @return {@code true} if the VR state changed.
+     */
+    public boolean onVrModeChanged(ActivityRecord record) {
+        // This message means that the top focused activity enabled VR mode (or an activity
+        // that previously set this has become focused).
+        VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            // VR mode isn't supported on this device.
+            return false;
+        }
+        boolean vrMode;
+        ComponentName requestedPackage;
+        ComponentName callingPackage;
+        int userId;
+        boolean changed = false;
+        synchronized (mGlobalAmLock) {
+            vrMode = record.requestedVrComponent != null;
+            requestedPackage = record.requestedVrComponent;
+            userId = record.userId;
+            callingPackage = record.info.getComponentName();
+
+            // Tell the VrController that a VR mode change is requested.
+            changed = changeVrModeLocked(vrMode, record.app);
+        }
+
+        // Tell VrManager that a VR mode changed is requested, VrManager will handle
+        // notifying all non-AM dependencies if needed.
+        vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
+        return changed;
+    }
+
+    /**
+     * Called to set an application's VR thread.
+     *
+     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
+     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
+     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
+     * scheduling for the previous thread will be unaffected.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock and the
+     *     mPidsSelfLocked object locks held.
+     *
+     * @param tid the tid of the thread to set, or 0 to unset the current thread.
+     * @param pid the pid of the process owning the thread to set.
+     * @param proc the ProcessRecord of the process owning the thread to set.
+     */
+    public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+        if (hasPersistentVrFlagSet()) {
+            Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
+            return;
+        }
+        if (proc == null) {
+           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
+           return;
+        }
+        if (tid != 0) {
+            enforceThreadInProcess(tid, pid);
+        }
+        if (!inVrMode()) {
+            Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
+        } else {
+            setVrRenderThreadLocked(tid, proc.curSchedGroup, false);
+        }
+        proc.vrThreadTid = (tid > 0) ? tid : 0;
+    }
+
+    /**
+     * Called to set an application's persistent VR thread.
+     *
+     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
+     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
+     * the scheduling for the previous thread will be unaffected.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock and the
+     *     mPidsSelfLocked object locks held.
+     *
+     * @param tid the tid of the thread to set, or 0 to unset the current thread.
+     * @param pid the pid of the process owning the thread to set.
+     * @param proc the ProcessRecord of the process owning the thread to set.
+     */
+    public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+        if (!hasPersistentVrFlagSet()) {
+            Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
+            return;
+        }
+        if (proc == null) {
+           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
+           return;
+        }
+        if (tid != 0) {
+            enforceThreadInProcess(tid, pid);
+        }
+        setPersistentVrRenderThreadLocked(tid, false);
+    }
+
+    /**
+     * Return {@code true} when UI features incompatible with VR mode should be disabled.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock held.
+     */
+    public boolean shouldDisableNonVrUiLocked() {
+        return mVrState != FLAG_NON_VR_MODE;
+    }
+
+    /**
+     * Called when to update this VrController instance's state when the system VR mode is being
+     * changed.
+     *
+     * <p>Note: This must be called with the global ActivityManagerService lock held.
+     *
+     * @param vrMode {@code true} if the system VR mode is being enabled.
+     * @param proc the ProcessRecord of the process enabling the system VR mode.
+     *
+     * @return {@code true} if our state changed.
+     */
+    private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) {
+        final int oldVrState = mVrState;
+
+        // This is the only place where mVrState should have its FLAG_VR_MODE setting
+        // changed.
+        if (vrMode) {
+            mVrState |= FLAG_VR_MODE;
+        } else {
+            mVrState &= ~FLAG_VR_MODE;
+        }
+
+        boolean changed = (oldVrState != mVrState);
+
+        if (changed) {
+            if (proc != null) {
+                if (proc.vrThreadTid > 0) {
+                    setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false);
+                }
+            } else {
+              clearVrRenderThreadLocked(false);
+            }
+        }
+        return changed;
+    }
+
+    /**
+     * Set the given thread as the new VR thread, and give it special scheduling priority.
+     *
+     * <p>If the current thread is this thread, do nothing. If the current thread is different from
+     * the given thread, the current thread will be returned to a normal scheduling priority.
+     *
+     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return the tid of the thread configured to run at the scheduling priority for VR
+     *          mode after this call completes (this may be the previous thread).
+     */
+    private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) {
+        if (mVrRenderThreadTid == newTid) {
+            return mVrRenderThreadTid;
+        }
+
+        if (mVrRenderThreadTid > 0) {
+            ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs);
+            mVrRenderThreadTid = 0;
+        }
+
+        if (newTid > 0) {
+            mVrRenderThreadTid = newTid;
+            ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs);
+        }
+        return mVrRenderThreadTid;
+    }
+
+    /**
+     * Set special scheduling for the given application persistent VR thread, if allowed.
+     *
+     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
+     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
+     * the scheduling for the previous thread will be unaffected.
+     *
+     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return the tid of the thread configured to run at the scheduling priority for VR
+     *          mode after this call completes (this may be the previous thread).
+     */
+    private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) {
+       if (!hasPersistentVrFlagSet()) {
+            if (!suppressLogs) {
+                Slog.w(TAG, "Failed to set persistent VR thread, "
+                        + "system not in persistent VR mode.");
+            }
+            return mVrRenderThreadTid;
+        }
+        return updateVrRenderThreadLocked(newTid, suppressLogs);
+    }
+
+    /**
+     * Set special scheduling for the given application VR thread, if allowed.
+     *
+     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
+     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
+     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
+     * scheduling for the previous thread will be unaffected.
+     *
+     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
+     * @param schedGroup the current scheduling group of the thread to set.
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     *
+     * @return the tid of the thread configured to run at the scheduling priority for VR
+     *          mode after this call completes (this may be the previous thread).
+     */
+    private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) {
+        boolean inVr = inVrMode();
+        boolean inPersistentVr = hasPersistentVrFlagSet();
+        if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+            if (!suppressLogs) {
+               String reason = "caller is not the current top application.";
+               if (!inVr) {
+                   reason = "system not in VR mode.";
+               } else if (inPersistentVr) {
+                   reason = "system in persistent VR mode.";
+               }
+               Slog.w(TAG, "Failed to set VR thread, " + reason);
+            }
+            return mVrRenderThreadTid;
+        }
+        return updateVrRenderThreadLocked(newTid, suppressLogs);
+    }
+
+    /**
+     * Unset any special scheduling used for the current VR render thread, and return it to normal
+     * scheduling priority.
+     *
+     * @param suppressLogs {@code true} if any error logging should be disabled.
+     */
+    private void clearVrRenderThreadLocked(boolean suppressLogs) {
+        updateVrRenderThreadLocked(0, suppressLogs);
+    }
+
+    /**
+     * Check that the given tid is running in the process for the given pid, and throw an exception
+     * if not.
+     */
+    private void enforceThreadInProcess(int tid, int pid) {
+        if (!Process.isThreadInProcess(pid, tid)) {
+            throw new IllegalArgumentException("VR thread does not belong to process");
+        }
+    }
+
+    /**
+     * True when the system is in VR mode.
+     */
+    private boolean inVrMode() {
+        return (mVrState & FLAG_VR_MODE) != 0;
+    }
+
+    /**
+     * True when the persistent VR mode flag has been set.
+     *
+     * Note: Currently this does not necessarily mean that the system is in VR mode.
+     */
+    private boolean hasPersistentVrFlagSet() {
+        return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 44c715b..64ee1e9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1294,19 +1294,14 @@
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
-    private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
-            boolean fromAssistant) {
+    private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel) {
         if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
             // cancel
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
                     UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
                     null);
         }
-        if (fromAssistant) {
-            mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
-        } else {
-            mRankingHelper.updateNotificationChannel(pkg, uid, channel);
-        }
+        mRankingHelper.updateNotificationChannel(pkg, uid, channel);
 
         synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
@@ -1709,7 +1704,7 @@
                 NotificationChannel channel) {
             enforceSystemOrSystemUI("Caller not system or systemui");
             Preconditions.checkNotNull(channel);
-            updateNotificationChannelInt(pkg, uid, channel, false);
+            updateNotificationChannelInt(pkg, uid, channel);
         }
 
         @Override
@@ -2646,47 +2641,6 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
-
-        @Override
-        public void createNotificationChannelFromAssistant(INotificationListener token, String pkg,
-                NotificationChannel channel) throws RemoteException {
-            ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            mRankingHelper.createNotificationChannel(pkg, uid, channel, false /* fromTargetApp */);
-            savePolicyFile();
-        }
-
-        @Override
-        public void deleteNotificationChannelFromAssistant(INotificationListener token, String pkg,
-                String channelId) throws RemoteException {
-            ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
-                throw new IllegalArgumentException("Cannot delete default channel");
-            }
-
-            int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
-                    info.userid, REASON_CHANNEL_BANNED, null);
-            mRankingHelper.deleteNotificationChannel(pkg, uid, channelId);
-            savePolicyFile();
-        }
-
-        @Override
-        public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg,
-                NotificationChannel channel) throws RemoteException {
-            ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            Preconditions.checkNotNull(channel);
-            int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            updateNotificationChannelInt(pkg, uid, channel, true);
-        }
-
-        @Override
-        public ParceledListSlice<NotificationChannel> getNotificationChannelsFromAssistant(
-                INotificationListener token, String pkg) throws RemoteException {
-            ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */);
-        }
     };
 
     private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
@@ -2695,17 +2649,10 @@
         }
         if (adjustment.getSignals() != null) {
             Bundle.setDefusable(adjustment.getSignals(), true);
-            final String overrideChannelId =
-                    adjustment.getSignals().getString(Adjustment.KEY_CHANNEL_ID, null);
             final ArrayList<String> people =
                     adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
             final ArrayList<SnoozeCriterion> snoozeCriterionList =
                     adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
-            if (!TextUtils.isEmpty(overrideChannelId)) {
-                n.updateNotificationChannel(mRankingHelper.getNotificationChannel(
-                        n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId,
-                        false /* includeDeleted */));
-            }
             n.setPeopleOverride(people);
             n.setSnoozeCriteria(snoozeCriterionList);
         }
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index e13df19..4d19b52 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -37,7 +37,6 @@
     void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp);
     void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
-    void updateNotificationChannelFromAssistant(String pkg, int uid, NotificationChannel channel);
     NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, int uid, String channelId);
     void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 65aaee0..b63b05f 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -609,55 +609,6 @@
     }
 
     @Override
-    public void updateNotificationChannelFromAssistant(String pkg, int uid,
-            NotificationChannel updatedChannel) {
-        Record r = getOrCreateRecord(pkg, uid);
-        if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
-        }
-        NotificationChannel channel = r.channels.get(updatedChannel.getId());
-        if (channel == null || channel.isDeleted()) {
-            throw new IllegalArgumentException("Channel does not exist");
-        }
-
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0) {
-            channel.setImportance(updatedChannel.getImportance());
-        }
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
-            channel.enableLights(updatedChannel.shouldShowLights());
-            channel.setLightColor(updatedChannel.getLightColor());
-        }
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_PRIORITY) == 0) {
-            channel.setBypassDnd(updatedChannel.canBypassDnd());
-        }
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) {
-            channel.setSound(updatedChannel.getSound(), updatedChannel.getAudioAttributes());
-        }
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
-            channel.enableVibration(updatedChannel.shouldVibrate());
-            channel.setVibrationPattern(updatedChannel.getVibrationPattern());
-        }
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VISIBILITY) == 0) {
-            if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
-                channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
-            } else {
-                channel.setLockscreenVisibility(updatedChannel.getLockscreenVisibility());
-            }
-        }
-        if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SHOW_BADGE) == 0) {
-            channel.setShowBadge(updatedChannel.canShowBadge());
-        }
-        if (updatedChannel.isDeleted()) {
-            channel.setDeleted(true);
-        }
-        // Assistant cannot change the group
-
-        MetricsLogger.action(getChannelLog(channel, pkg));
-        r.channels.put(channel.getId(), channel);
-        updateConfig();
-    }
-
-    @Override
     public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
             boolean includeDeleted) {
         Preconditions.checkNotNull(pkg);
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 07c9dec..2100038 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -98,4 +98,8 @@
     public boolean isInstant() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0;
     }
+
+    public boolean isRuntimeOnly() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index f6e96b2..c4cc4fb 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -64,9 +64,10 @@
     private volatile boolean mBindRequested;
     private IInstantAppResolver mRemoteInstance;
 
-    public EphemeralResolverConnection(Context context, ComponentName componentName) {
+    public EphemeralResolverConnection(
+            Context context, ComponentName componentName, String action) {
         mContext = context;
-        mIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE).setComponent(componentName);
+        mIntent = new Intent(action).setComponent(componentName);
     }
 
     public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[],
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 0ae5f31..89a303d 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -272,6 +272,7 @@
             } else {
                 // Deleting an app prunes all instant state such as cookie
                 deleteDir(getInstantApplicationDir(pkg.packageName, userId));
+                mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
                 removeAppLPw(userId, ps.appId);
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index acbd446..781be34 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.os.WorkSource;
@@ -259,9 +260,11 @@
         // 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)) {
+        if (info.deviceProtectedDataDir != null &&
+                FileUtils.contains(info.deviceProtectedDataDir, path)) {
             dexoptFlags |= DEXOPT_STORAGE_DE;
-        } else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
+        } else if (info.credentialProtectedDataDir != null &&
+                FileUtils.contains(info.credentialProtectedDataDir, path)) {
             dexoptFlags |= DEXOPT_STORAGE_CE;
         } else {
             Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f62f115..ce4d7ee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -849,8 +849,7 @@
     /** Component used to show resolver settings for Instant Apps */
     final ComponentName mInstantAppResolverSettingsComponent;
 
-    /** Component used to install ephemeral applications */
-    ComponentName mInstantAppInstallerComponent;
+    /** Activity used to install instant applications */
     ActivityInfo mInstantAppInstallerActivity;
     final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
 
@@ -2056,6 +2055,7 @@
             }
             if (bp != null && (bp.isRuntime() || bp.isDevelopment())
                     && (!instantApp || bp.isInstant())
+                    && (supportsRuntimePermissions || !bp.isRuntimeOnly())
                     && (grantedPermissions == null
                            || ArrayUtils.contains(grantedPermissions, permission))) {
                 final int flags = permissionsState.getPermissionFlags(permission, userId);
@@ -2813,20 +2813,22 @@
             }
 
             mInstallerService = new PackageInstallerService(context, this);
-            final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
-            if (ephemeralResolverComponent != null) {
+            final Pair<ComponentName, String> instantAppResolverComponent =
+                    getInstantAppResolverLPr();
+            if (instantAppResolverComponent != null) {
                 if (DEBUG_EPHEMERAL) {
-                    Slog.d(TAG, "Set ephemeral resolver: " + ephemeralResolverComponent);
+                    Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
                 }
-                mInstantAppResolverConnection =
-                        new EphemeralResolverConnection(mContext, ephemeralResolverComponent);
+                mInstantAppResolverConnection = new EphemeralResolverConnection(
+                        mContext, instantAppResolverComponent.first,
+                        instantAppResolverComponent.second);
                 mInstantAppResolverSettingsComponent =
-                        getEphemeralResolverSettingsLPr(ephemeralResolverComponent);
+                        getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);
             } else {
                 mInstantAppResolverConnection = null;
                 mInstantAppResolverSettingsComponent = null;
             }
-            updateInstantAppInstallerLocked();
+            updateInstantAppInstallerLocked(null);
 
             // Read and update the usage of dex files.
             // Do this at the end of PM init so that all the packages have their
@@ -2866,22 +2868,15 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    private void updateInstantAppInstallerLocked() {
-        final ComponentName oldInstantAppInstallerComponent = mInstantAppInstallerComponent;
-        final ActivityInfo newInstantAppInstaller = getEphemeralInstallerLPr();
-        ComponentName newInstantAppInstallerComponent = newInstantAppInstaller == null
-                ? null : newInstantAppInstaller.getComponentName();
-
-        if (newInstantAppInstallerComponent != null
-                && !newInstantAppInstallerComponent.equals(oldInstantAppInstallerComponent)) {
-            if (DEBUG_EPHEMERAL) {
-                Slog.d(TAG, "Set ephemeral installer: " + newInstantAppInstallerComponent);
-            }
-            setUpInstantAppInstallerActivityLP(newInstantAppInstaller);
-        } else if (DEBUG_EPHEMERAL && newInstantAppInstallerComponent == null) {
-            Slog.d(TAG, "Unset ephemeral installer; none available");
+    private void updateInstantAppInstallerLocked(String modifiedPackage) {
+        // we're only interested in updating the installer appliction when 1) it's not
+        // already set or 2) the modified package is the installer
+        if (mInstantAppInstallerActivity != null
+                && !mInstantAppInstallerActivity.getComponentName().getPackageName()
+                        .equals(modifiedPackage)) {
+            return;
         }
-        mInstantAppInstallerComponent = newInstantAppInstallerComponent;
+        setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
     }
 
     private static File preparePackageParserCache(boolean isUpgrade) {
@@ -3045,7 +3040,7 @@
         }
     }
 
-    private @Nullable ComponentName getEphemeralResolverLPr() {
+    private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() {
         final String[] packageArray =
                 mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
         if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
@@ -3060,7 +3055,8 @@
                 MATCH_DIRECT_BOOT_AWARE
                 | MATCH_DIRECT_BOOT_UNAWARE
                 | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
-        final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
+        String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE;
+        final Intent resolverIntent = new Intent(actionName);
         List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
                 resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
         // temporarily look for the old action
@@ -3068,7 +3064,8 @@
             if (DEBUG_EPHEMERAL) {
                 Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
             }
-            resolverIntent.setAction(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
+            actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE;
+            resolverIntent.setAction(actionName);
             resolvers = queryIntentServicesInternal(resolverIntent, null,
                     resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
         }
@@ -3101,7 +3098,7 @@
                 Slog.v(TAG, "Ephemeral resolver found;"
                         + " pkg: " + packageName + ", info:" + info);
             }
-            return new ComponentName(packageName, info.serviceInfo.name);
+            return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
         }
         if (DEBUG_EPHEMERAL) {
             Slog.v(TAG, "Ephemeral resolver NOT found");
@@ -3109,7 +3106,7 @@
         return null;
     }
 
-    private @Nullable ActivityInfo getEphemeralInstallerLPr() {
+    private @Nullable ActivityInfo getInstantAppInstallerLPr() {
         final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
@@ -3151,7 +3148,7 @@
         }
     }
 
-    private @Nullable ComponentName getEphemeralResolverSettingsLPr(
+    private @Nullable ComponentName getInstantAppResolverSettingsLPr(
             @NonNull ComponentName resolver) {
         final Intent intent =  new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
                 .addCategory(Intent.CATEGORY_DEFAULT)
@@ -5732,7 +5729,7 @@
         if (mInstantAppResolverConnection == null) {
             return false;
         }
-        if (mInstantAppInstallerComponent == null) {
+        if (mInstantAppInstallerActivity == null) {
             return false;
         }
         if (intent.getComponent() != null) {
@@ -11411,6 +11408,8 @@
         for (int i=0; i<N; i++) {
             final String name = pkg.requestedPermissions.get(i);
             final BasePermission bp = mSettings.mPermissions.get(name);
+            final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+                    >= Build.VERSION_CODES.M;
 
             if (DEBUG_INSTALL) {
                 Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
@@ -11432,6 +11431,12 @@
                 continue;
             }
 
+            if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+                Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package "
+                        + pkg.packageName);
+                continue;
+            }
+
             final String perm = bp.name;
             boolean allowedSig = false;
             int grant = GRANT_DENIED;
@@ -11447,8 +11452,6 @@
             }
 
             final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-            final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
-                    >= Build.VERSION_CODES.M;
             switch (level) {
                 case PermissionInfo.PROTECTION_NORMAL: {
                     // For all apps normal permissions are install time ones.
@@ -17081,7 +17084,7 @@
 
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                 updateSequenceNumberLP(pkgName, res.newUsers);
-                updateInstantAppInstallerLocked();
+                updateInstantAppInstallerLocked(pkgName);
             }
         }
     }
@@ -17657,7 +17660,7 @@
                         mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
                     }
                     updateSequenceNumberLP(packageName, info.removedUsers);
-                    updateInstantAppInstallerLocked();
+                    updateInstantAppInstallerLocked(packageName);
                 }
             }
         }
@@ -20020,7 +20023,7 @@
             updateSequenceNumberLP(packageName, new int[] { userId });
             final long callingId = Binder.clearCallingIdentity();
             try {
-                updateInstantAppInstallerLocked();
+                updateInstantAppInstallerLocked(packageName);
             } finally {
                 Binder.restoreCallingIdentity(callingId);
             }
@@ -23212,7 +23215,8 @@
         @Override
         public boolean isInstantAppInstallerComponent(ComponentName component) {
             synchronized (mPackages) {
-                return component != null && component.equals(mInstantAppInstallerComponent);
+                return mInstantAppInstallerActivity != null
+                        && mInstantAppInstallerActivity.getComponentName().equals(component);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index c693a47..3d7cedc 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -19,7 +19,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
+import android.os.FileUtils;
 import android.os.RemoteException;
 import android.os.storage.StorageManager;
 import android.os.UserHandle;
@@ -93,7 +93,7 @@
      * Note that this method is invoked when apps load dex files and it should
      * return as fast as possible.
      *
-     * @param loadingPackage the package performing the load
+     * @param loadingAppInfo the package performing the load
      * @param dexPaths the list of dex files being loaded
      * @param loaderIsa the ISA of the app loading the dex files
      * @param loaderUserId the user id which runs the code loading the dex files
@@ -191,8 +191,7 @@
             throw new IllegalArgumentException(
                 "notifyPackageInstalled called with USER_ALL");
         }
-        cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
-                pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
+        cachePackageInfo(pi, userId);
     }
 
     /**
@@ -231,13 +230,32 @@
         }
     }
 
-    public void cachePackageCodeLocation(String packageName, String baseCodePath,
-            String[] splitCodePaths, String dataDir, int userId) {
+    /**
+     * Caches the code location from the given package info.
+     */
+    private void cachePackageInfo(PackageInfo pi, int userId) {
+        ApplicationInfo ai = pi.applicationInfo;
+        String[] dataDirs = new String[] {ai.dataDir, ai.deviceProtectedDataDir,
+                ai.credentialProtectedDataDir};
+        cachePackageCodeLocation(pi.packageName, ai.sourceDir, ai.splitSourceDirs,
+                dataDirs, userId);
+    }
+
+    private void cachePackageCodeLocation(String packageName, String baseCodePath,
+            String[] splitCodePaths, String[] dataDirs, int userId) {
         PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
                 new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
         pcl.updateCodeLocation(baseCodePath, splitCodePaths);
-        if (dataDir != null) {
-            pcl.mergeAppDataDirs(dataDir, userId);
+        if (dataDirs != null) {
+            for (String dataDir : dataDirs) {
+                // The set of data dirs includes deviceProtectedDataDir and
+                // credentialProtectedDataDir which might be null for shared
+                // libraries. Currently we don't track these but be lenient
+                // and check in case we ever decide to store their usage data.
+                if (dataDir != null) {
+                    pcl.mergeAppDataDirs(dataDir, userId);
+                }
+            }
         }
     }
 
@@ -250,8 +268,7 @@
             int userId = entry.getKey();
             for (PackageInfo pi : packageInfoList) {
                 // Cache the code locations.
-                cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
-                        pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
+                cachePackageInfo(pi, userId);
 
                 // Cache a map from package name to the set of user ids who installed the package.
                 // We will use it to sync the data and remove obsolete entries from
@@ -329,6 +346,7 @@
                 mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
                 continue;
             }
+
             int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
                     dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
             success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
@@ -350,7 +368,7 @@
             // Nothing to reconcile.
             return;
         }
-        Set<String> dexFilesToRemove = new HashSet<>();
+
         boolean updated = false;
         for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
             String dexPath = entry.getKey();
@@ -378,14 +396,16 @@
             }
             ApplicationInfo info = pkg.applicationInfo;
             int flags = 0;
-            if (info.dataDir.equals(info.deviceProtectedDataDir)) {
+            if (info.deviceProtectedDataDir != null &&
+                    FileUtils.contains(info.deviceProtectedDataDir, dexPath)) {
                 flags |= StorageManager.FLAG_STORAGE_DE;
-            } else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
+            } else if (info.credentialProtectedDataDir!= null &&
+                    FileUtils.contains(info.credentialProtectedDataDir, dexPath)) {
                 flags |= StorageManager.FLAG_STORAGE_CE;
             } else {
-                Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
-                updated = mPackageDexUsage.removeUserPackage(
-                        packageName, dexUseInfo.getOwnerUserId()) || updated;
+                Slog.e(TAG, "Could not infer CE/DE storage for path " + dexPath);
+                updated = mPackageDexUsage.removeDexFile(
+                        packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated;
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 95fb5af..6633efd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5255,8 +5255,11 @@
             }
         }
 
+        // Don't allow snapshots to influence SystemUI visibility flags.
+        // TODO: Revisit this once SystemUI flags for snapshots are handled correctly
         boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
-                && attrs.type < FIRST_SYSTEM_WINDOW;
+                && attrs.type < FIRST_SYSTEM_WINDOW
+                && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0;
         final int stackId = win.getStackId();
         if (mTopFullscreenOpaqueWindowState == null && visible) {
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 2bc3c5f..4b4be40 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -566,7 +566,7 @@
             return false;
         }
 
-        mContainer.startingData = new SnapshotStartingData(mService, snapshot);
+        mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot());
         scheduleAddStartingWindow();
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4ebf1fc..21be742 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -665,7 +665,8 @@
                     }
                 }
             }
-            if (!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) {
+            if ((!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) ||
+                    winAnimator.isDummyAnimation()) {
                 // Updates the shown frame before we set up the surface. This is needed
                 // because the resizing could change the top-left position (in addition to
                 // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
@@ -2920,11 +2921,7 @@
                     if (stack != null) {
                         stack.getBounds(frame);
                     }
-
-                    // We want to screenshot with the exact bounds of the surface of the app. Thus,
-                    // intersect it with the frame.
-                    frame.intersect(w.mFrame);
-                }else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
+                } else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
                     final Rect wf = w.mFrame;
                     final Rect cr = w.mContentInsets;
                     int left = wf.left + cr.left;
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 35f35db..e73d4d25 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.GraphicBuffer;
 import android.view.WindowManagerPolicy.StartingSurface;
 
@@ -26,9 +25,9 @@
 class SnapshotStartingData extends StartingData {
 
     private final WindowManagerService mService;
-    private final TaskSnapshot mSnapshot;
+    private final GraphicBuffer mSnapshot;
 
-    SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) {
+    SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) {
         super(service);
         mService = service;
         mSnapshot = snapshot;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b816d81..3ffb093 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,14 +17,15 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static com.android.server.EventLogTags.WM_TASK_REMOVED;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -39,6 +40,7 @@
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 48b01f4..b8d0b8c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -28,7 +28,6 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
 import android.os.Environment;
 import android.util.ArraySet;
 import android.view.WindowManagerPolicy.StartingSurface;
@@ -153,7 +152,7 @@
      * MANAGER LOCK WHEN CALLING THIS METHOD!
      */
     StartingSurface createStartingSurface(AppWindowToken token,
-            TaskSnapshot snapshot) {
+            GraphicBuffer snapshot) {
         return TaskSnapshotSurface.create(mService, token, snapshot);
     }
 
@@ -167,17 +166,8 @@
         if (buffer == null) {
             return null;
         }
-        final WindowState mainWindow = top.findMainWindow();
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
-                minRect(mainWindow.mContentInsets, mainWindow.mStableInsets), false /* reduced */,
-                1f /* scale */);
-    }
-
-    private Rect minRect(Rect rect1, Rect rect2) {
-        return new Rect(Math.min(rect1.left, rect2.left),
-                Math.min(rect1.top, rect2.top),
-                Math.min(rect1.right, rect2.right),
-                Math.min(rect1.bottom, rect2.bottom));
+                top.findMainWindow().mStableInsets, false /* reduced */, 1f /* scale */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 1591e48..04403e2 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -16,35 +16,20 @@
 
 package com.android.server.wm;
 
-import static android.graphics.Color.WHITE;
-import static android.graphics.Color.alpha;
-import static android.view.SurfaceControl.HIDDEN;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
-import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.getColorViewLeftInset;
-import static com.android.internal.policy.DecorView.getColorViewTopInset;
-import static com.android.internal.policy.DecorView.getNavigationBarRect;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.GraphicBuffer;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -52,22 +37,17 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.view.IWindowSession;
 import android.view.Surface;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy.StartingSurface;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DecorView;
 import com.android.internal.view.BaseIWindow;
 
 /**
@@ -77,57 +57,19 @@
  */
 class TaskSnapshotSurface implements StartingSurface {
 
-    private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
-
-    /**
-     * When creating the starting window, we use the exact same layout flags such that we end up
-     * with a window with the exact same dimensions etc. However, these flags are not used in layout
-     * and might cause other side effects so we exclude them.
-     */
-    private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
-            | FLAG_NOT_TOUCHABLE
-            | FLAG_NOT_TOUCH_MODAL
-            | FLAG_ALT_FOCUSABLE_IM
-            | FLAG_NOT_FOCUSABLE
-            | FLAG_HARDWARE_ACCELERATED
-            | FLAG_IGNORE_CHEEK_PRESSES
-            | FLAG_LOCAL_FOCUS_MODE
-            | FLAG_SLIPPERY
-            | FLAG_WATCH_OUTSIDE_TOUCH
-            | FLAG_SPLIT_TOUCH
-            | FLAG_SCALED
-            | FLAG_SECURE;
-
     private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
     private static final int MSG_REPORT_DRAW = 0;
     private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
     private final Window mWindow;
     private final Surface mSurface;
-    private SurfaceControl mChildSurfaceControl;
     private final IWindowSession mSession;
     private final WindowManagerService mService;
-    private final Rect mTaskBounds;
-    private final Rect mStableInsets = new Rect();
-    private final Rect mContentInsets = new Rect();
-    private final Rect mFrame = new Rect();
-    private final TaskSnapshot mSnapshot;
-    private final CharSequence mTitle;
     private boolean mHasDrawn;
     private boolean mReportNextDraw;
-    private long mShownTime;
-    private final Handler mHandler;
-    private final boolean mSizeMismatch;
-    private final Paint mBackgroundPaint = new Paint();
-    private final Paint mStatusBarPaint = new Paint();
-    private final Paint mNavigationBarPaint = new Paint();
-    private final int mStatusBarColor;
-    private final int mNavigationBarColor;
-    private final int mSysUiVis;
-    private final int mWindowFlags;
-    private final int mWindowPrivateFlags;
+    private Paint mFillBackgroundPaint = new Paint();
 
     static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
-            TaskSnapshot snapshot) {
+            GraphicBuffer snapshot) {
 
         final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
         final Window window = new Window();
@@ -136,51 +78,32 @@
         final Surface surface = new Surface();
         final Rect tmpRect = new Rect();
         final Rect tmpFrame = new Rect();
-        final Rect taskBounds;
-        final Rect tmpContentInsets = new Rect();
-        final Rect tmpStableInsets = new Rect();
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
-        int backgroundColor = WHITE;
-        int statusBarColor = 0;
-        int navigationBarColor = 0;
-        final int sysUiVis;
-        final int windowFlags;
-        final int windowPrivateFlags;
+        int fillBackgroundColor = Color.WHITE;
         synchronized (service.mWindowMap) {
-            final WindowState mainWindow = token.findMainWindow();
-            if (mainWindow == null) {
-                Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
-                        + token);
-                return null;
-            }
-            sysUiVis = mainWindow.getSystemUiVisibility();
-            windowFlags = mainWindow.getAttrs().flags;
-            windowPrivateFlags = mainWindow.getAttrs().privateFlags;
-
             layoutParams.type = TYPE_APPLICATION_STARTING;
-            layoutParams.format = snapshot.getSnapshot().getFormat();
-            layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
+            layoutParams.format = snapshot.getFormat();
+            layoutParams.flags = FLAG_LAYOUT_INSET_DECOR
+                    | FLAG_LAYOUT_IN_SCREEN
                     | FLAG_NOT_FOCUSABLE
-                    | FLAG_NOT_TOUCHABLE;
+                    | FLAG_NOT_TOUCHABLE
+                    | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
             layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT;
             layoutParams.token = token.token;
             layoutParams.width = LayoutParams.MATCH_PARENT;
             layoutParams.height = LayoutParams.MATCH_PARENT;
-            layoutParams.systemUiVisibility = sysUiVis;
+
+            // TODO: Inherit behavior whether to draw behind status bar/nav bar.
+            layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
             final Task task = token.getTask();
             if (task != null) {
-                layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
+                layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId));
 
                 final TaskDescription taskDescription = task.getTaskDescription();
                 if (taskDescription != null) {
-                    backgroundColor = taskDescription.getBackgroundColor();
-                    statusBarColor = taskDescription.getStatusBarColor();
-                    navigationBarColor = taskDescription.getNavigationBarColor();
+                    fillBackgroundColor = taskDescription.getBackgroundColor();
                 }
-                taskBounds = new Rect();
-                task.getBounds(taskBounds);
-            } else {
-                taskBounds = null;
             }
         }
         try {
@@ -195,57 +118,31 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
-                navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds);
+                surface, fillBackgroundColor);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
-                    tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpMergedConfiguration, surface);
+                    tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration,
+                    surface);
         } catch (RemoteException e) {
             // Local call.
         }
-        snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
-        snapshotSurface.drawSnapshot();
+        snapshotSurface.drawSnapshot(snapshot);
         return snapshotSurface;
     }
 
     @VisibleForTesting
     TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
-            TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
-            int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
-            Rect taskBounds) {
+            int fillBackgroundColor) {
         mService = service;
-        mHandler = new Handler(mService.mH.getLooper());
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
         mSurface = surface;
-        mSnapshot = snapshot;
-        mTitle = title;
-        mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
-        mTaskBounds = taskBounds;
-        mSysUiVis = sysUiVis;
-        mWindowFlags = windowFlags;
-        mWindowPrivateFlags = windowPrivateFlags;
-        mSizeMismatch = (mFrame.width() != snapshot.getSnapshot().getWidth()
-                || mFrame.height() != snapshot.getSnapshot().getHeight());
-        mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags,
-                service.mContext.getColor(R.color.system_bar_background_semi_transparent),
-                statusBarColor);
-        mNavigationBarColor = navigationBarColor;
-        mStatusBarPaint.setColor(mStatusBarColor);
-        mNavigationBarPaint.setColor(navigationBarColor);
+        mFillBackgroundPaint.setColor(fillBackgroundColor);
     }
 
     @Override
     public void remove() {
-        synchronized (mService.mWindowMap) {
-            final long now = SystemClock.uptimeMillis();
-            if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
-                mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
-                return;
-            }
-        }
         try {
             mSession.remove(mWindow);
         } catch (RemoteException e) {
@@ -253,149 +150,31 @@
         }
     }
 
-    @VisibleForTesting
-    void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
-        mFrame.set(frame);
-        mContentInsets.set(contentInsets);
-        mStableInsets.set(stableInsets);
-    }
-
-    private void drawSnapshot() {
-        final GraphicBuffer buffer = mSnapshot.getSnapshot();
-        if (mSizeMismatch) {
-            // The dimensions of the buffer and the window don't match, so attaching the buffer
-            // will fail. Better create a child window with the exact dimensions and fill the parent
-            // window with the background color!
-            drawSizeMismatchSnapshot(buffer);
-        } else {
-            drawSizeMatchSnapshot(buffer);
-        }
+    private void drawSnapshot(GraphicBuffer snapshot) {
+        mSurface.attachAndQueueBuffer(snapshot);
         final boolean reportNextDraw;
         synchronized (mService.mWindowMap) {
-            mShownTime = SystemClock.uptimeMillis();
             mHasDrawn = true;
             reportNextDraw = mReportNextDraw;
         }
         if (reportNextDraw) {
             reportDrawn();
         }
-    }
-
-    private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
-        mSurface.attachAndQueueBuffer(buffer);
-        mSurface.release();
-    }
-
-    private void drawSizeMismatchSnapshot(GraphicBuffer buffer) {
-        final SurfaceSession session = new SurfaceSession(mSurface);
-
-        // Keep a reference to it such that it doesn't get destroyed when finalized.
-        mChildSurfaceControl = new SurfaceControl(session,
-                mTitle + " - task-snapshot-surface",
-                buffer.getWidth(), buffer.getHeight(), buffer.getFormat(), HIDDEN);
-        Surface surface = new Surface();
-        surface.copyFrom(mChildSurfaceControl);
-
-        // Clip off ugly navigation bar.
-        final Rect crop = calculateSnapshotCrop();
-        final Rect frame = calculateSnapshotFrame(crop);
-        SurfaceControl.openTransaction();
-        try {
-            // We can just show the surface here as it will still be hidden as the parent is
-            // still hidden.
-            mChildSurfaceControl.show();
-            mChildSurfaceControl.setWindowCrop(crop);
-            mChildSurfaceControl.setPosition(frame.left, frame.top);
-        } finally {
-            SurfaceControl.closeTransaction();
-        }
-        surface.attachAndQueueBuffer(buffer);
-        surface.release();
-
-        final Canvas c = mSurface.lockCanvas(null);
-        drawBackgroundAndBars(c, frame);
-        mSurface.unlockCanvasAndPost(c);
         mSurface.release();
     }
 
     @VisibleForTesting
-    Rect calculateSnapshotCrop() {
-        final Rect rect = new Rect();
-        rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
-        final Rect insets = mSnapshot.getContentInsets();
-
-        // Let's remove all system decorations except the status bar, but only if the task is at the
-        // very top of the screen.
-        rect.inset(insets.left, mTaskBounds.top != 0 ? insets.top : 0, insets.right, insets.bottom);
-        return rect;
-    }
-
-    @VisibleForTesting
-    Rect calculateSnapshotFrame(Rect crop) {
-        final Rect frame = new Rect(crop);
-
-        // By default, offset it to to top/left corner
-        frame.offsetTo(-crop.left, -crop.top);
-
-        // However, we also need to make space for the navigation bar on the left side.
-        final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
-                mContentInsets.left);
-        frame.offset(colorViewLeftInset, 0);
-        return frame;
-    }
-
-    @VisibleForTesting
-    void drawBackgroundAndBars(Canvas c, Rect frame) {
-        final int statusBarHeight = getStatusBarColorViewHeight();
-        final boolean fillHorizontally = c.getWidth() > frame.right;
-        final boolean fillVertically = c.getHeight() > frame.bottom;
+    void fillEmptyBackground(Canvas c, Bitmap b) {
+        final boolean fillHorizontally = c.getWidth() > b.getWidth();
+        final boolean fillVertically = c.getHeight() > b.getHeight();
         if (fillHorizontally) {
-            c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
-                    c.getWidth(), fillVertically
-                            ? frame.bottom
-                            : c.getHeight(),
-                    mBackgroundPaint);
+            c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically
+                        ? b.getHeight()
+                        : c.getHeight(),
+                    mFillBackgroundPaint);
         }
         if (fillVertically) {
-            c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
-        }
-        drawStatusBarBackground(c, frame, statusBarHeight);
-        drawNavigationBarBackground(c);
-    }
-
-    private int getStatusBarColorViewHeight() {
-        final boolean forceStatusBarBackground =
-                (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
-        if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
-                mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) {
-            return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
-        } else {
-            return 0;
-        }
-    }
-
-    private boolean isNavigationBarColorViewVisible() {
-        return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
-                mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */);
-    }
-
-    @VisibleForTesting
-    void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) {
-        if (statusBarHeight > 0 && c.getWidth() > frame.right) {
-            final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
-                    mContentInsets.right);
-            c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
-        }
-    }
-
-    @VisibleForTesting
-    void drawNavigationBarBackground(Canvas c) {
-        final Rect navigationBarRect = new Rect();
-        getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
-                navigationBarRect);
-        final boolean visible = isNavigationBarColorViewVisible();
-        if (visible && !navigationBarRect.isEmpty()) {
-            c.drawRect(navigationBarRect, mNavigationBarPaint);
+            c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint);
         }
     }
 
@@ -432,10 +211,10 @@
         }
     };
 
-    @VisibleForTesting
-    static class Window extends BaseIWindow {
+    private static class Window extends BaseIWindow {
 
         private TaskSnapshotSurface mOuter;
+
         public void setOuter(TaskSnapshotSurface outer) {
             mOuter = outer;
         }
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index c976fe5..d834e25 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -929,15 +929,15 @@
                                   db.jniInfo.contextHubInfoCtor);
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId);
 
-    jstrBuf = env->NewStringUTF(hub.name);
+    jstrBuf = env->NewStringUTF(hub.name.c_str());
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf);
     env->DeleteLocalRef(jstrBuf);
 
-    jstrBuf = env->NewStringUTF(hub.vendor);
+    jstrBuf = env->NewStringUTF(hub.vendor.c_str());
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf);
     env->DeleteLocalRef(jstrBuf);
 
-    jstrBuf = env->NewStringUTF(hub.toolchain);
+    jstrBuf = env->NewStringUTF(hub.toolchain.c_str());
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf);
     env->DeleteLocalRef(jstrBuf);
 
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/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 40af2f8..ad593be 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -497,149 +497,6 @@
                 new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
     }
 
-    @Test
-    public void testUpdate_userLockedImportance() throws Exception {
-        // all fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        // same id, try to update
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
-
-    @Test
-    public void testUpdate_userLockedVisibility() throws Exception {
-        // all fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-        channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        // same id, try to update
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
-
-    @Test
-    public void testUpdate_userLockedVibration() throws Exception {
-        // all fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.enableLights(false);
-        channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        // same id, try to update
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.enableVibration(true);
-        channel2.setVibrationPattern(new long[]{100});
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
-
-    @Test
-    public void testUpdate_userLockedLights() throws Exception {
-        // all fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.enableLights(false);
-        channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        // same id, try to update
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.enableLights(true);
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
-
-    @Test
-    public void testUpdate_userLockedPriority() throws Exception {
-        // all fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setBypassDnd(true);
-        channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        // same id, try to update all fields
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.setBypassDnd(false);
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
-
-    @Test
-    public void testUpdate_userLockedRingtone() throws Exception {
-        // all fields locked by user
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        // same id, try to update all fields
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
-
-    @Test
-    public void testUpdate_userLockedBadge() throws Exception {
-        final NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel.setShowBadge(true);
-        channel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
-
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
-
-        final NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
-        channel2.setShowBadge(false);
-
-        mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel2);
-
-        // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-    }
 
     @Test
     public void testUpdate() throws Exception {
@@ -816,30 +673,6 @@
     }
 
     @Test
-    public void testUpdateDeletedChannels() throws Exception {
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
-
-        mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
-        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
-        try {
-            mHelper.updateNotificationChannel(PKG, UID, channel);
-            fail("Updated deleted channel");
-        } catch (IllegalArgumentException e) {
-            // :)
-        }
-
-        try {
-            mHelper.updateNotificationChannelFromAssistant(PKG, UID, channel);
-            fail("Updated deleted channel");
-        } catch (IllegalArgumentException e) {
-            // :)
-        }
-    }
-
-    @Test
     public void testCreateDeletedChannel() throws Exception {
         long[] vibration = new long[]{100, 67, 145, 156};
         NotificationChannel channel =
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index bb4507d..d47a67c 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -27,6 +27,10 @@
     ShortcutManagerTestUtils \
     truth-prebuilt
 
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
+
+LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl
+
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_PACKAGE_NAME := FrameworksServicesTests
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 205c8de..cc682c4 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -48,6 +48,8 @@
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk
new file mode 100644
index 0000000..0c9b839
--- /dev/null
+++ b/services/tests/servicestests/aidl/Android.mk
@@ -0,0 +1,23 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := \
+        com/android/servicestests/aidl/INetworkStateObserver.aidl
+LOCAL_MODULE := servicestests-aidl
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl b/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl
new file mode 100644
index 0000000..ca9fc4c
--- /dev/null
+++ b/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.servicestests.aidl;
+
+oneway interface INetworkStateObserver {
+    /**
+     * {@param resultData} will be in the format
+     * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo.
+     * For detailed info, see
+     * servicestests/test-apps/ConnTestApp/.../ConnTestActivity#checkNetworkStatus
+     */
+    void onNetworkStateChecked(String resultData);
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/conntestapp b/services/tests/servicestests/res/raw/conntestapp
new file mode 100644
index 0000000..6093303
--- /dev/null
+++ b/services/tests/servicestests/res/raw/conntestapp
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 092c60b..6701b71 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -137,7 +137,7 @@
         mHandler = new TestHandler(mHandlerThread.getLooper());
         mInjector = new TestInjector();
         mAms = new ActivityManagerService(mInjector);
-        mAms.mWaitForNetworkTimeoutMs = 100;
+        mAms.mWaitForNetworkTimeoutMs = 2000;
 
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
new file mode 100644
index 0000000..19defe1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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 com.android.server.am.ActivityManagerService.Injector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.AppOpsService;
+
+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.io.File;
+
+/**
+ * Test class for {@link CoreSettingsObserver}.
+ *
+ * To run the tests, use
+ *
+ * runtest -c com.android.server.am.CoreSettingsObserverTest frameworks-services
+ *
+ * or the following steps:
+ *
+ * Build: m FrameworksServicesTests
+ * Install: adb install -r \
+ *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Run: adb shell am instrument -e class com.android.server.am.CoreSettingsObserverTest -w \
+ *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CoreSettingsObserverTest {
+    private static final String TEST_SETTING_SECURE_INT = "secureInt";
+    private static final String TEST_SETTING_GLOBAL_FLOAT = "globalFloat";
+    private static final String TEST_SETTING_SYSTEM_STRING = "systemString";
+
+    private static final int TEST_INT = 111;
+    private static final float TEST_FLOAT = 3.14f;
+    private static final String TEST_STRING = "testString";
+
+    private ActivityManagerService mAms;
+    @Mock private Context mContext;
+
+    private MockContentResolver mContentResolver;
+    private CoreSettingsObserver mCoreSettingsObserver;
+
+    @BeforeClass
+    public static void setupOnce() {
+        CoreSettingsObserver.sSecureSettingToTypeMap.put(TEST_SETTING_SECURE_INT, int.class);
+        CoreSettingsObserver.sGlobalSettingToTypeMap.put(TEST_SETTING_GLOBAL_FLOAT, float.class);
+        CoreSettingsObserver.sSystemSettingToTypeMap.put(TEST_SETTING_SYSTEM_STRING, String.class);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        final Context originalContext = InstrumentationRegistry.getContext();
+        when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
+        mContentResolver = new MockContentResolver(mContext);
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+        mAms = new ActivityManagerService(new TestInjector());
+        mCoreSettingsObserver = new CoreSettingsObserver(mAms);
+    }
+
+    @Test
+    public void testPopulateSettings() {
+        Settings.Secure.putInt(mContentResolver, TEST_SETTING_SECURE_INT, TEST_INT);
+        Settings.Global.putFloat(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, TEST_FLOAT);
+        Settings.System.putString(mContentResolver, TEST_SETTING_SYSTEM_STRING, TEST_STRING);
+
+        final Bundle settingsBundle = getPopulatedBundle();
+
+        assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
+                TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
+        assertEquals("Unexpected value of " + TEST_SETTING_GLOBAL_FLOAT,
+                TEST_FLOAT, settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT), 0);
+        assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
+                TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
+    }
+
+    @Test
+    public void testPopulateSettings_settingNotSet() {
+        final Bundle settingsBundle = getPopulatedBundle();
+
+        assertFalse("Bundle should not contain " + TEST_SETTING_SECURE_INT,
+                settingsBundle.containsKey(TEST_SETTING_SECURE_INT));
+        assertFalse("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT,
+                settingsBundle.containsKey(TEST_SETTING_GLOBAL_FLOAT));
+        assertFalse("Bundle should not contain " + TEST_SETTING_SYSTEM_STRING,
+                settingsBundle.containsKey(TEST_SETTING_SYSTEM_STRING));
+    }
+
+    private Bundle getPopulatedBundle() {
+        final Bundle settingsBundle = new Bundle();
+        mCoreSettingsObserver.populateSettings(settingsBundle,
+                CoreSettingsObserver.sGlobalSettingToTypeMap);
+        mCoreSettingsObserver.populateSettings(settingsBundle,
+                CoreSettingsObserver.sSecureSettingToTypeMap);
+        mCoreSettingsObserver.populateSettings(settingsBundle,
+                CoreSettingsObserver.sSystemSettingToTypeMap);
+        return settingsBundle;
+    }
+
+    private class TestInjector extends Injector {
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return null;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return null;
+        }
+    }
+}
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/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
new file mode 100644
index 0000000..f971971
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
@@ -0,0 +1,443 @@
+/*
+ * 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.net;
+
+import static android.util.DebugUtils.valueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.frameworks.servicestests.R;
+import com.android.servicestests.aidl.INetworkStateObserver;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for verifying network availability on activity start.
+ *
+ * To run the tests, use
+ *
+ * runtest -c com.android.server.net.ConnOnActivityStartTest frameworks-services
+ *
+ * or the following steps:
+ *
+ * Build: m FrameworksServicesTests
+ * Install: adb install -r \
+ *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Run: adb shell am instrument -e class com.android.server.net.ConnOnActivityStartTest -w \
+ *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ConnOnActivityStartTest {
+    private static final String TAG = ConnOnActivityStartTest.class.getSimpleName();
+
+    private static final String ACTION_INSTALL_COMPLETE = "com.android.server.net.INSTALL_COMPLETE";
+
+    private static final String TEST_APP_URI =
+            "android.resource://com.android.frameworks.servicestests/raw/conntestapp";
+    private static final String TEST_PKG = "com.android.servicestests.apps.conntestapp";
+    private static final String TEST_ACTIVITY_CLASS = TEST_PKG + ".ConnTestActivity";
+
+    private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH";
+
+    private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+
+    private static final int WAIT_FOR_INSTALL_TIMEOUT_MS = 2000; // 2 sec
+
+    private static final int NETWORK_CHECK_TIMEOUT_MS = 6000; // 6 sec
+
+    private static final int SCREEN_ON_DELAY_MS = 500; // 0.5 sec
+
+    private static final String NETWORK_STATUS_SEPARATOR = "\\|";
+
+    private static final int REPEAT_TEST_COUNT = 5;
+
+    private static Context mContext;
+    private static UiDevice mUiDevice;
+    private static int mTestPkgUid;
+
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+        installAppAndAssertInstalled();
+        mContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+        mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        mContext.getPackageManager().deletePackage(TEST_PKG,
+                new IPackageDeleteObserver.Stub() {
+                    @Override
+                    public void packageDeleted(String packageName, int returnCode)
+                            throws RemoteException {
+                        Log.e(TAG, packageName + " deleted, returnCode: " + returnCode);
+                    }
+                }, 0);
+    }
+
+    @Test
+    public void testStartActivity_batterySaver() throws Exception {
+        setBatterySaverMode(true);
+        try {
+            testConnOnActivityStart("testStartActivity_batterySaver");
+        } finally {
+            setBatterySaverMode(false);
+        }
+    }
+
+    @Test
+    public void testStartActivity_dataSaver() throws Exception {
+        setDataSaverMode(true);
+        try {
+            testConnOnActivityStart("testStartActivity_dataSaver");
+        } finally {
+            setDataSaverMode(false);
+        }
+    }
+
+    @Test
+    public void testStartActivity_dozeMode() throws Exception {
+        setDozeMode(true);
+        try {
+            testConnOnActivityStart("testStartActivity_dozeMode");
+        } finally {
+            setDozeMode(false);
+        }
+    }
+
+    @Test
+    public void testStartActivity_appStandby() throws Exception {
+        try{
+            turnBatteryOff();
+            setAppIdle(true);
+            SystemClock.sleep(30000);
+            turnScreenOn();
+            startActivityAndCheckNetworkAccess();
+        } finally {
+            turnBatteryOn();
+            setAppIdle(false);
+        }
+    }
+
+    @Test
+    public void testStartActivity_backgroundRestrict() throws Exception {
+        updateRestrictBackgroundBlacklist(true);
+        try {
+            testConnOnActivityStart("testStartActivity_backgroundRestrict");
+        } finally {
+            updateRestrictBackgroundBlacklist(false);
+        }
+    }
+
+    private void testConnOnActivityStart(String testName) throws Exception {
+        for (int i = 1; i <= REPEAT_TEST_COUNT; ++i) {
+            try {
+                Log.d(TAG, testName + " Start #" + i);
+                turnScreenOn();
+                SystemClock.sleep(SCREEN_ON_DELAY_MS);
+                startActivityAndCheckNetworkAccess();
+                Log.d(TAG, testName + " end #" + i);
+            } finally {
+                finishActivity();
+            }
+        }
+    }
+
+    // TODO: Some of these methods are also used in CTS, so instead of duplicating code,
+    // create a static library which can be used by both servicestests and cts.
+    private void setBatterySaverMode(boolean enabled) throws Exception {
+        if (enabled) {
+            turnBatteryOff();
+            executeCommand("settings put global low_power 1");
+        } else {
+            executeCommand("settings put global low_power 0");
+            turnBatteryOn();
+        }
+        final String result = executeCommand("settings get global low_power");
+        assertEquals(enabled ? "1" : "0", result);
+    }
+
+    private void setDataSaverMode(boolean enabled) throws Exception {
+        executeCommand("cmd netpolicy set restrict-background " + enabled);
+        final String output = executeCommand("cmd netpolicy get restrict-background");
+        final String expectedSuffix = enabled ? "enabled" : "disabled";
+        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+                output.endsWith(expectedSuffix));
+    }
+
+    private void setDozeMode(boolean enabled) throws Exception {
+        if (enabled) {
+            turnBatteryOff();
+            turnScreenOff();
+            executeCommand("dumpsys deviceidle force-idle deep");
+        } else {
+            turnScreenOn();
+            turnBatteryOn();
+            executeCommand("dumpsys deviceidle unforce");
+        }
+        assertDelayedCommandResult("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE",
+                5 /* maxTries */, 500 /* napTimeMs */);
+    }
+
+    private void setAppIdle(boolean enabled) throws Exception {
+        executeCommand("am set-inactive " + TEST_PKG + " " + enabled);
+        assertDelayedCommandResult("am get-inactive " + TEST_PKG, "Idle=" + enabled,
+                10 /* maxTries */, 2000 /* napTimeMs */);
+    }
+
+    private void updateRestrictBackgroundBlacklist(boolean add) throws Exception {
+        if (add) {
+            executeCommand("cmd netpolicy add restrict-background-blacklist " + mTestPkgUid);
+        } else {
+            executeCommand("cmd netpolicy remove restrict-background-blacklist " + mTestPkgUid);
+        }
+        assertRestrictBackground("restrict-background-blacklist", mTestPkgUid, add);
+    }
+
+    private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
+        final int maxTries = 5;
+        boolean actual = false;
+        final String expectedUid = Integer.toString(uid);
+        String uids = "";
+        for (int i = 1; i <= maxTries; i++) {
+            final String output = executeCommand("cmd netpolicy list " + list);
+            uids = output.split(":")[1];
+            for (String candidate : uids.split(" ")) {
+                actual = candidate.trim().equals(expectedUid);
+                if (expected == actual) {
+                    return;
+                }
+            }
+            Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected "
+                    + expected + ", got " + actual + "); sleeping 1s before polling again");
+            SystemClock.sleep(1000);
+        }
+        fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual
+                + ". Full list: " + uids);
+    }
+
+    private void turnBatteryOff() throws Exception {
+        executeCommand("cmd battery unplug");
+    }
+
+    private void turnBatteryOn() throws Exception {
+        executeCommand("cmd battery reset");
+    }
+
+    private void turnScreenOff() throws Exception {
+        executeCommand("input keyevent KEYCODE_SLEEP");
+    }
+
+    private void turnScreenOn() throws Exception {
+        executeCommand("input keyevent KEYCODE_WAKEUP");
+        executeCommand("wm dismiss-keyguard");
+    }
+
+    private String executeCommand(String cmd) throws IOException {
+        final String result = mUiDevice.executeShellCommand(cmd).trim();
+        Log.d(TAG, String.format("Result for '%s': %s", cmd, result));
+        return result;
+    }
+
+    private void assertDelayedCommandResult(String cmd, String expectedResult,
+            int maxTries, int napTimeMs) throws IOException {
+        String result = "";
+        for (int i = 1; i <= maxTries; ++i) {
+            result = executeCommand(cmd);
+            if (expectedResult.equals(result)) {
+                return;
+            }
+            Log.v(TAG, "Command '" + cmd + "' returned '" + result + " instead of '"
+                    + expectedResult + "' on attempt #" + i
+                    + "; sleeping " + napTimeMs + "ms before trying again");
+            SystemClock.sleep(napTimeMs);
+        }
+        fail("Command '" + cmd + "' did not return '" + expectedResult + "' after "
+                + maxTries + " attempts. Last result: '" + result + "'");
+    }
+
+    private void startActivityAndCheckNetworkAccess() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final Intent launchIntent = new Intent().setComponent(
+                new ComponentName(TEST_PKG, TEST_ACTIVITY_CLASS));
+        final Bundle extras = new Bundle();
+        final String[] errors = new String[] {null};
+        extras.putBinder(EXTRA_NETWORK_STATE_OBSERVER, new INetworkStateObserver.Stub() {
+            @Override
+            public void onNetworkStateChecked(String resultData) {
+                errors[0] = checkForAvailability(resultData);
+                latch.countDown();
+            }
+        });
+        launchIntent.putExtras(extras);
+        mContext.startActivity(launchIntent);
+        if (latch.await(NETWORK_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            if (!errors[0].isEmpty()) {
+                fail("Network not available for test app " + mTestPkgUid);
+            }
+        } else {
+            fail("Timed out waiting for network availability status from test app " + mTestPkgUid);
+        }
+    }
+
+    private void finishActivity() {
+        final Intent finishIntent = new Intent(ACTION_FINISH_ACTIVITY)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mContext.sendBroadcast(finishIntent);
+    }
+
+    private String checkForAvailability(String resultData) {
+        if (resultData == null) {
+            assertNotNull("Network status from app2 is null, Uid: " + mTestPkgUid, resultData);
+        }
+        // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
+        final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
+        assertEquals("Wrong network status: " + resultData + ", Uid: " + mTestPkgUid,
+                5, parts.length); // Sanity check
+        final NetworkInfo.State state = parts[0].equals("null")
+                ? null : NetworkInfo.State.valueOf(parts[0]);
+        final NetworkInfo.DetailedState detailedState = parts[1].equals("null")
+                ? null : NetworkInfo.DetailedState.valueOf(parts[1]);
+        final boolean connected = Boolean.valueOf(parts[2]);
+        final String connectionCheckDetails = parts[3];
+        final String networkInfo = parts[4];
+
+        final StringBuilder errors = new StringBuilder();
+        final NetworkInfo.State expectedState = NetworkInfo.State.CONNECTED;
+        final NetworkInfo.DetailedState expectedDetailedState = NetworkInfo.DetailedState.CONNECTED;
+
+        if (true != connected) {
+            errors.append(String.format("External site connection failed: expected %s, got %s\n",
+                    true, connected));
+        }
+        if (expectedState != state || expectedDetailedState != detailedState) {
+            errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n",
+                    expectedState, expectedDetailedState, state, detailedState));
+        }
+
+        if (errors.length() > 0) {
+            errors.append("\tnetworkInfo: " + networkInfo + "\n");
+            errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n");
+        }
+        return errors.toString();
+    }
+
+    private static void installAppAndAssertInstalled() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final int[] result = {PackageInstaller.STATUS_SUCCESS};
+        final BroadcastReceiver installStatusReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String pkgName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME);
+                if (!TEST_PKG.equals(pkgName)) {
+                    return;
+                }
+                result[0] = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PackageInstaller.STATUS_FAILURE);
+                latch.countDown();
+            }
+        };
+        mContext.registerReceiver(installStatusReceiver, new IntentFilter(ACTION_INSTALL_COMPLETE));
+        try {
+            installApp();
+            if (latch.await(WAIT_FOR_INSTALL_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                if (result[0] != PackageInstaller.STATUS_SUCCESS) {
+                    fail("Couldn't install test app, result: "
+                            + valueToString(PackageInstaller.class, "STATUS_", result[0]));
+                }
+            } else {
+                fail("Timed out waiting for the test app to install");
+            }
+        } finally {
+            mContext.unregisterReceiver(installStatusReceiver);
+        }
+    }
+
+    private static void installApp() throws Exception {
+        final Uri packageUri = Uri.parse(TEST_APP_URI);
+        final InputStream in = mContext.getContentResolver().openInputStream(packageUri);
+
+        final PackageInstaller packageInstaller
+                = mContext.getPackageManager().getPackageInstaller();
+        final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        params.setAppPackageName(TEST_PKG);
+
+        final int sessionId = packageInstaller.createSession(params);
+        final PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+
+        OutputStream out = null;
+        try {
+            out = session.openWrite(TAG, 0, -1);
+            final byte[] buffer = new byte[65536];
+            int c;
+            while ((c = in.read(buffer)) != -1) {
+                out.write(buffer, 0, c);
+            }
+            session.fsync(out);
+        } finally {
+            IoUtils.closeQuietly(in);
+            IoUtils.closeQuietly(out);
+        }
+        session.commit(createIntentSender(mContext, sessionId));
+    }
+
+    private static IntentSender createIntentSender(Context context, int sessionId) {
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                context, sessionId, new Intent(ACTION_INSTALL_COMPLETE), 0);
+        return pendingIntent.getIntentSender();
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 72fb78e..afc0f67 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -360,6 +360,19 @@
         assertNull(mDexManager.getPackageUseInfo(frameworkDex));
     }
 
+    @Test
+    public void testNotifySecondaryFromProtected() {
+        // Foo loads its own secondary files.
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+        assertNotNull(pui);
+        assertFalse(pui.isUsedByOtherApps());
+        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
         for (String dex : secondaries) {
@@ -394,6 +407,8 @@
         ai.setBaseCodePath(codeDir + "/base.dex");
         ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
         ai.dataDir = "/data/user/" + userId + "/" + packageName;
+        ai.deviceProtectedDataDir = "/data/user_de/" + userId + "/" + packageName;
+        ai.credentialProtectedDataDir = "/data/user_ce/" + userId + "/" + packageName;
         ai.packageName = packageName;
         return ai;
     }
@@ -426,6 +441,13 @@
             return paths;
         }
 
+        List<String> getSecondaryDexPathsFromProtectedDirs() {
+            List<String> paths = new ArrayList<>();
+            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
+            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+            return paths;
+        }
+
         List<String> getBaseAndSplitDexPaths() {
             List<String> paths = new ArrayList<>();
             paths.add(mPackageInfo.applicationInfo.sourceDir);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 717ddf2..aab75ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -16,9 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -27,19 +24,15 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.Surface;
 
-import com.android.server.wm.TaskSnapshotSurface.Window;
-
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -55,174 +48,59 @@
 
     private TaskSnapshotSurface mSurface;
 
-    private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
-            int windowFlags, Rect taskBounds) {
-        final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
-                GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER);
-        final TaskSnapshot snapshot = new TaskSnapshot(buffer,
-                ORIENTATION_PORTRAIT, contentInsets, false, 1.0f);
-        mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test",
-                Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds);
-    }
-
-    private void setupSurface(int width, int height) {
-        setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                new Rect(0, 0, width, height));
+    @Before
+    public void setUp() {
+        mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE);
     }
 
     @Test
     public void fillEmptyBackground_fillHorizontally() throws Exception {
-        setupSurface(200, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(200);
         when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
+        final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
         verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
     }
 
     @Test
     public void fillEmptyBackground_fillVertically() throws Exception {
-        setupSurface(100, 200);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
         when(mockCanvas.getHeight()).thenReturn(200);
-        mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
+        final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
         verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
     }
 
     @Test
     public void fillEmptyBackground_fillBoth() throws Exception {
-        setupSurface(200, 200);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(200);
         when(mockCanvas.getHeight()).thenReturn(200);
-        mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
+        final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
         verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
         verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
     }
 
     @Test
     public void fillEmptyBackground_dontFill_sameSize() throws Exception {
-        setupSurface(100, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
         when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
+        final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
         verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
     }
 
     @Test
     public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception {
-        setupSurface(100, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
         when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
+        final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
         verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
     }
-
-    @Test
-    public void testCalculateSnapshotCrop() {
-        setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop());
-    }
-
-    @Test
-    public void testCalculateSnapshotCrop_taskNotOnTop() {
-        setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100));
-        assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
-    }
-
-    @Test
-    public void testCalculateSnapshotCrop_navBarLeft() {
-        setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop());
-    }
-
-    @Test
-    public void testCalculateSnapshotCrop_navBarRight() {
-        setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop());
-    }
-
-    @Test
-    public void testCalculateSnapshotFrame() {
-        setupSurface(100, 100);
-        final Rect insets = new Rect(0, 10, 0, 10);
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        assertEquals(new Rect(0, -10, 100, 70),
-                mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
-    }
-
-    @Test
-    public void testCalculateSnapshotFrame_navBarLeft() {
-        setupSurface(100, 100);
-        final Rect insets = new Rect(10, 10, 0, 0);
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        assertEquals(new Rect(0, -10, 90, 80),
-                mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
-    }
-
-    @Test
-    public void testDrawStatusBarBackground() {
-        setupSurface(100, 100);
-        final Rect insets = new Rect(0, 10, 10, 0);
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        final Canvas mockCanvas = mock(Canvas.class);
-        when(mockCanvas.getWidth()).thenReturn(100);
-        when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10);
-        verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
-    }
-
-    @Test
-    public void testDrawStatusBarBackground_nope() {
-        setupSurface(100, 100);
-        final Rect insets = new Rect(0, 10, 10, 0);
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        final Canvas mockCanvas = mock(Canvas.class);
-        when(mockCanvas.getWidth()).thenReturn(100);
-        when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10);
-        verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
-    }
-
-    @Test
-    public void testDrawNavigationBarBackground() {
-        final Rect insets = new Rect(0, 10, 0, 10);
-        setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                new Rect(0, 0, 100, 100));
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        final Canvas mockCanvas = mock(Canvas.class);
-        when(mockCanvas.getWidth()).thenReturn(100);
-        when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawNavigationBarBackground(mockCanvas);
-        verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
-    }
-
-    @Test
-    public void testDrawNavigationBarBackground_left() {
-        final Rect insets = new Rect(10, 10, 0, 0);
-        setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                new Rect(0, 0, 100, 100));
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        final Canvas mockCanvas = mock(Canvas.class);
-        when(mockCanvas.getWidth()).thenReturn(100);
-        when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawNavigationBarBackground(mockCanvas);
-        verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
-    }
-
-    @Test
-    public void testDrawNavigationBarBackground_right() {
-        final Rect insets = new Rect(0, 10, 10, 0);
-        setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
-                new Rect(0, 0, 100, 100));
-        mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
-        final Canvas mockCanvas = mock(Canvas.class);
-        when(mockCanvas.getWidth()).thenReturn(100);
-        when(mockCanvas.getHeight()).thenReturn(100);
-        mSurface.drawNavigationBarBackground(mockCanvas);
-        verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
-    }
 }
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
new file mode 100644
index 0000000..02afe83
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ConnTestApp
+LOCAL_CERTIFICATE := platform
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..0da3562
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.conntestapp">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application>
+        <activity android:name=".ConnTestActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
new file mode 100644
index 0000000..11ebfca
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
@@ -0,0 +1,170 @@
+/*
+ * 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.servicestests.apps.conntestapp;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.servicestests.aidl.INetworkStateObserver;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class ConnTestActivity extends Activity {
+    private static final String TAG = ConnTestActivity.class.getSimpleName();
+
+    private static final String TEST_PKG = ConnTestActivity.class.getPackage().getName();
+    private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH";
+    private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+
+    private static final int NETWORK_TIMEOUT_MS = 5 * 1000;
+
+    private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s";
+
+    private BroadcastReceiver finishCommandReceiver = null;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        notifyNetworkStateObserver();
+
+        finishCommandReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                ConnTestActivity.this.finish();
+            }
+        };
+        registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
+    }
+
+    @Override
+    public void onStop() {
+        if (finishCommandReceiver != null) {
+            unregisterReceiver(finishCommandReceiver);
+        }
+        super.onStop();
+    }
+
+    private void notifyNetworkStateObserver() {
+        if (getIntent() == null) {
+            return;
+        }
+
+        final Bundle extras = getIntent().getExtras();
+        if (extras == null) {
+            return;
+        }
+        final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface(
+                extras.getBinder(EXTRA_NETWORK_STATE_OBSERVER));
+        if (observer != null) {
+            AsyncTask.execute(() -> {
+                try {
+                    observer.onNetworkStateChecked(checkNetworkStatus(ConnTestActivity.this));
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error occured while notifying the observer: " + e);
+                }
+            });
+        }
+    }
+
+    /**
+     * Checks whether the network is available and return a string which can then be send as a
+     * result data for the ordered broadcast.
+     *
+     * <p>
+     * The string has the following format:
+     *
+     * <p><pre><code>
+     * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo
+     * </code></pre>
+     *
+     * <p>Where:
+     *
+     * <ul>
+     * <li>{@code NetinfoState}: enum value of {@link NetworkInfo.State}.
+     * <li>{@code NetinfoDetailedState}: enum value of {@link NetworkInfo.DetailedState}.
+     * <li>{@code RealConnectionCheck}: boolean value of a real connection check (i.e., an attempt
+     *     to access an external website.
+     * <li>{@code RealConnectionCheckDetails}: if HTTP output core or exception string of the real
+     *     connection attempt
+     * <li>{@code Netinfo}: string representation of the {@link NetworkInfo}.
+     * </ul>
+     *
+     * For example, if the connection was established fine, the result would be something like:
+     * <p><pre><code>
+     * CONNECTED|CONNECTED|true|200|[type: WIFI[], state: CONNECTED/CONNECTED, reason: ...]
+     * </code></pre>
+     */
+    private String checkNetworkStatus(Context context) {
+        final ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        final String address = "http://example.com";
+        final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+        Log.d(TAG, "Running checkNetworkStatus() on thread "
+                + Thread.currentThread().getName() + " for UID " + getUid(context)
+                + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+        boolean checkStatus = false;
+        String checkDetails = "N/A";
+        try {
+            final URL url = new URL(address);
+            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+            conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
+            conn.setRequestMethod("GET");
+            conn.setDoInput(true);
+            conn.connect();
+            final int response = conn.getResponseCode();
+            checkStatus = true;
+            checkDetails = "HTTP response for " + address + ": " + response;
+        } catch (Exception e) {
+            checkStatus = false;
+            checkDetails = "Exception getting " + address + ": " + e;
+        }
+        Log.d(TAG, checkDetails);
+        final String state, detailedState;
+        if (networkInfo != null) {
+            state = networkInfo.getState().name();
+            detailedState = networkInfo.getDetailedState().name();
+        } else {
+            state = detailedState = "null";
+        }
+        final String status = String.format(NETWORK_STATUS_TEMPLATE, state, detailedState,
+                Boolean.valueOf(checkStatus), checkDetails, networkInfo);
+        Log.d(TAG, "Offering " + status);
+        return status;
+    }
+
+    private int getUid(Context context) {
+        final String packageName = context.getPackageName();
+        try {
+            return context.getPackageManager().getPackageUid(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Could not get UID for " + packageName, e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index bb11dab..baee21c 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -413,6 +413,11 @@
     global _emoji_sequences, _emoji_zwj_sequences
     _emoji_properties = parse_unicode_datafile(
         path.join(ucd_path, 'emoji-data.txt'), reverse=True)
+    emoji_properties_additions = parse_unicode_datafile(
+        path.join(ucd_path, 'additions', 'emoji-data.txt'), reverse=True)
+    for prop in emoji_properties_additions.keys():
+        _emoji_properties[prop].update(emoji_properties_additions[prop])
+
     _chars_by_age = parse_unicode_datafile(
         path.join(ucd_path, 'DerivedAge.txt'), reverse=True)
     sequences = parse_standardized_variants(
@@ -420,6 +425,7 @@
     _text_variation_sequences, _emoji_variation_sequences = sequences
     _emoji_sequences = parse_unicode_datafile(
         path.join(ucd_path, 'emoji-sequences.txt'))
+
     _emoji_zwj_sequences = parse_unicode_datafile(
         path.join(ucd_path, 'emoji-zwj-sequences.txt'))
     _emoji_zwj_sequences.update(parse_unicode_datafile(
@@ -450,22 +456,6 @@
 
 COMBINING_KEYCAP = 0x20E3
 
-# Characters that Android defaults to emoji style, different from the recommendations in UTR #51
-ANDROID_DEFAULT_EMOJI = frozenset({
-    0x2600, # BLACK SUN WITH RAYS
-    0x2601, # CLOUD
-    0x260E, # BLACK TELEPHONE
-    0x261D, # WHITE UP POINTING INDEX
-    0x263A, # WHITE SMILING FACE
-    0x2660, # BLACK SPADE SUIT
-    0x2663, # BLACK CLUB SUIT
-    0x2665, # BLACK HEART SUIT
-    0x2666, # BLACK DIAMOND SUIT
-    0x270C, # VICTORY HAND
-    0x2744, # SNOWFLAKE
-    0x2764, # HEAVY BLACK HEART
-})
-
 LEGACY_ANDROID_EMOJI = {
     0xFE4E5: flag_sequence('JP'),
     0xFE4E6: flag_sequence('US'),
@@ -554,7 +544,6 @@
         set(LEGACY_ANDROID_EMOJI.keys()))
     default_emoji = (
         _emoji_properties['Emoji_Presentation'] |
-        ANDROID_DEFAULT_EMOJI |
         all_sequences |
         set(LEGACY_ANDROID_EMOJI.keys()))