Merge "Adjusted margins of Ongoing App Ops Dialog"
diff --git a/Android.bp b/Android.bp
index f40aab1..839d4cd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -296,6 +296,7 @@
         "core/java/android/service/notification/IConditionListener.aidl",
         "core/java/android/service/notification/IConditionProvider.aidl",
         "core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+        "core/java/android/service/sms/IFinancialSmsService.aidl",
         "core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
         "core/java/android/service/vr/IVrListener.aidl",
         "core/java/android/service/vr/IVrManager.aidl",
@@ -348,6 +349,7 @@
         "core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
         "core/java/android/view/autofill/IAutoFillManager.aidl",
         "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
+        "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
         "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
         "core/java/android/view/intelligence/IIntelligenceManager.aidl",
         "core/java/android/view/IApplicationToken.aidl",
@@ -724,6 +726,7 @@
         "android.hardware.wifi-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
         "android.hardware.radio-V1.3-java",
+        "android.hardware.radio-V1.4-java",
         "android.hardware.usb.gadget-V1.0-java",
         "netd_aidl_interface-java",
     ],
@@ -859,7 +862,7 @@
 java_library {
     name: "ext",
     installable: true,
-    sdk_version: "core_current",
+    no_framework_libs: true,
     static_libs: [
         "libphonenumber-platform",
         "nist-sip",
diff --git a/api/current.txt b/api/current.txt
index abd3c31..14214cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -159,9 +159,6 @@
     field public static final java.lang.String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS";
     field public static final deprecated java.lang.String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
     field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
-    field public static final java.lang.String WRITE_MEDIA_AUDIO = "android.permission.WRITE_MEDIA_AUDIO";
-    field public static final java.lang.String WRITE_MEDIA_IMAGES = "android.permission.WRITE_MEDIA_IMAGES";
-    field public static final java.lang.String WRITE_MEDIA_VIDEO = "android.permission.WRITE_MEDIA_VIDEO";
     field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
     field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
     field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
@@ -1319,6 +1316,7 @@
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
+    field public static final int supportsMultipleDisplays = 16844183; // 0x1010597
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
     field public static final int supportsRtl = 16843695; // 0x10103af
     field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
@@ -5229,6 +5227,7 @@
     ctor public Notification(android.os.Parcel);
     method public android.app.Notification clone();
     method public int describeContents();
+    method public boolean getAllowSystemGeneratedContextualActions();
     method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
     method public java.lang.String getChannelId();
@@ -5460,6 +5459,7 @@
     method public android.app.Notification.Style getStyle();
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+    method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
     method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
@@ -6352,6 +6352,7 @@
     method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
     method public boolean supportsAmbientMode();
+    method public boolean supportsMultipleDisplays();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
   }
@@ -10039,6 +10040,7 @@
     field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
     field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
+    field public static final java.lang.String ACTION_DEFINE = "android.intent.action.DEFINE";
     field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE";
     field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
     field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -10129,6 +10131,7 @@
     field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
     field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
     field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
+    field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
     field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
     field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
     field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
@@ -22639,7 +22642,7 @@
     method public abstract void onLocationChanged(android.location.Location);
     method public abstract void onProviderDisabled(java.lang.String);
     method public abstract void onProviderEnabled(java.lang.String);
-    method public abstract void onStatusChanged(java.lang.String, int, android.os.Bundle);
+    method public abstract deprecated void onStatusChanged(java.lang.String, int, android.os.Bundle);
   }
 
   public class LocationManager {
@@ -22651,7 +22654,7 @@
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
     method public void clearTestProviderLocation(java.lang.String);
-    method public void clearTestProviderStatus(java.lang.String);
+    method public deprecated void clearTestProviderStatus(java.lang.String);
     method public java.util.List<java.lang.String> getAllProviders();
     method public java.lang.String getBestProvider(android.location.Criteria, boolean);
     method public java.lang.String getGnssHardwareModelName();
@@ -22688,7 +22691,7 @@
     method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
     method public void setTestProviderEnabled(java.lang.String, boolean);
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
-    method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+    method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
     method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
@@ -22696,7 +22699,7 @@
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
     field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering";
-    field public static final java.lang.String KEY_STATUS_CHANGED = "status";
+    field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status";
     field public static final java.lang.String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
     field public static final java.lang.String NETWORK_PROVIDER = "network";
     field public static final java.lang.String PASSIVE_PROVIDER = "passive";
@@ -22715,9 +22718,9 @@
     method public boolean supportsAltitude();
     method public boolean supportsBearing();
     method public boolean supportsSpeed();
-    field public static final int AVAILABLE = 2; // 0x2
-    field public static final int OUT_OF_SERVICE = 0; // 0x0
-    field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
+    field public static final deprecated int AVAILABLE = 2; // 0x2
+    field public static final deprecated int OUT_OF_SERVICE = 0; // 0x0
+    field public static final deprecated int TEMPORARILY_UNAVAILABLE = 1; // 0x1
   }
 
   public abstract interface OnNmeaMessageListener {
@@ -22946,7 +22949,7 @@
     method public void adjustSuggestedStreamVolume(int, int, int);
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
-    method public static int generateAudioSessionId();
+    method public int generateAudioSessionId();
     method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
     method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
@@ -24586,6 +24589,7 @@
     field public static final java.lang.String KEY_WIDTH = "width";
     field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
     field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
+    field public static final java.lang.String MIMETYPE_AUDIO_AC4 = "audio/ac4";
     field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
     field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
     field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
@@ -29128,15 +29132,18 @@
     method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
     method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
     method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
-    method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
     method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsEnhancedOpen();
     method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
     method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
     method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
     method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
-    method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
     method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
     method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa2EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa2Passphrase(java.lang.String);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa3EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa3Passphrase(java.lang.String);
   }
 
   public final class WifiNetworkSuggestion implements android.os.Parcelable {
@@ -42882,6 +42889,7 @@
     field public static final java.lang.String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
     field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+    field public static final java.lang.String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
     field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
     field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
     field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
@@ -43229,9 +43237,9 @@
 
   public class MbmsGroupCallSession implements java.lang.AutoCloseable {
     method public void close();
-    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
     method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
-    method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback);
   }
 
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
@@ -44236,7 +44244,7 @@
   public class GroupCall implements java.lang.AutoCloseable {
     method public void close();
     method public long getTmgi();
-    method public void updateGroupCall(int[], int[]);
+    method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
     field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
     field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
     field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
@@ -44248,11 +44256,10 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
-  public class GroupCallCallback {
-    ctor public GroupCallCallback();
-    method public void onBroadcastSignalStrengthUpdated(int);
-    method public void onError(int, java.lang.String);
-    method public void onGroupCallStateChanged(int, int);
+  public abstract interface GroupCallCallback {
+    method public abstract void onBroadcastSignalStrengthUpdated(int);
+    method public abstract void onError(int, java.lang.String);
+    method public abstract void onGroupCallStateChanged(int, int);
     field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
   }
 
@@ -44292,6 +44299,11 @@
     field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
   }
 
+  public static class MbmsErrors.GroupCallErrors {
+    field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6
+    field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5
+  }
+
   public static class MbmsErrors.InitializationErrors {
     field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
     field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
@@ -44304,12 +44316,11 @@
     field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
   }
 
-  public class MbmsGroupCallSessionCallback {
-    ctor public MbmsGroupCallSessionCallback();
-    method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
-    method public void onError(int, java.lang.String);
-    method public void onMiddlewareReady();
-    method public void onServiceInterfaceAvailable(java.lang.String, int);
+  public abstract interface MbmsGroupCallSessionCallback {
+    method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+    method public abstract void onError(int, java.lang.String);
+    method public abstract void onMiddlewareReady();
+    method public abstract void onServiceInterfaceAvailable(java.lang.String, int);
   }
 
   public class MbmsStreamingSessionCallback {
@@ -45610,9 +45621,14 @@
     method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
   }
 
-  public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+  public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
     ctor public LineHeightSpan.Standard(int);
+    ctor public LineHeightSpan.Standard(android.os.Parcel);
     method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getSpanTypeId();
+    method public void writeToParcel(android.os.Parcel, int);
   }
 
   public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
@@ -49099,7 +49115,7 @@
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onProvideAutofillStructure(android.view.ViewStructure, int);
     method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
-    method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+    method public void onProvideContentCaptureStructure(android.view.ViewStructure, int);
     method public void onProvideStructure(android.view.ViewStructure);
     method public void onProvideVirtualStructure(android.view.ViewStructure);
     method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
@@ -49315,6 +49331,8 @@
     method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
     method public boolean startNestedScroll(int);
     method public void stopNestedScroll();
+    method public void transformMatrixToGlobal(android.graphics.Matrix);
+    method public void transformMatrixToLocal(android.graphics.Matrix);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable);
     method public final void updateDragShadow(android.view.View.DragShadowBuilder);
@@ -51907,6 +51925,7 @@
     method public java.time.ZonedDateTime getTime();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+    field public static final android.app.Person PERSON_USER_LOCAL;
   }
 
   public static final class ConversationActions.Message.Builder {
@@ -52175,6 +52194,7 @@
     field public static final int STATUS_LINKS_APPLIED = 0; // 0x0
     field public static final int STATUS_NO_LINKS_APPLIED = 2; // 0x2
     field public static final int STATUS_NO_LINKS_FOUND = 1; // 0x1
+    field public static final int STATUS_UNSUPPORTED_CHARACTER = 4; // 0x4
   }
 
   public static final class TextLinks.Builder {
@@ -54168,6 +54188,7 @@
     ctor public ImageView(android.content.Context, android.util.AttributeSet);
     ctor public ImageView(android.content.Context, android.util.AttributeSet, int);
     ctor public ImageView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void animateTransform(android.graphics.Matrix);
     method public final void clearColorFilter();
     method public boolean getAdjustViewBounds();
     method public boolean getBaselineAlignBottom();
diff --git a/api/system-current.txt b/api/system-current.txt
index 75a2cc4..a53ab3e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1225,7 +1225,6 @@
     method public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
-    method public android.content.pm.PackageInfo getPackageInfoAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -4938,12 +4937,47 @@
 
 package android.service.intelligence {
 
+  public final class FillCallback {
+    method public void onSuccess(android.service.intelligence.FillResponse);
+  }
+
+  public final class FillController {
+    method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+  }
+
+  public final class FillRequest {
+    method public android.view.autofill.AutofillId getFocusedId();
+    method public android.service.intelligence.PresentationParams getPresentationParams();
+    method public android.service.intelligence.InteractionSessionId getSessionId();
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.FillResponse> CREATOR;
+  }
+
+  public static class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.intelligence.FillResponse build();
+    method public android.service.intelligence.FillResponse.Builder setFillWindow(android.service.intelligence.FillWindow);
+    method public android.service.intelligence.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+  }
+
+  public final class FillWindow {
+    ctor public FillWindow();
+    method public void destroy();
+    method public boolean update(android.service.intelligence.PresentationParams.Area, android.view.View, long);
+    field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+  }
+
   public abstract class IntelligenceService extends android.app.Service {
     ctor public IntelligenceService();
     method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
     method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
     method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
     method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+    method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
   }
 
@@ -4965,6 +4999,23 @@
     field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
   }
 
+  public abstract class PresentationParams {
+    method public int getFlags();
+    method public android.service.intelligence.PresentationParams.Area getFullArea();
+    method public android.service.intelligence.PresentationParams.Area getSuggestionArea();
+    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+    field public static final int FLAG_HOST_IME = 16; // 0x10
+    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+  }
+
+  public static abstract class PresentationParams.Area {
+    method public android.graphics.Rect getBounds();
+    method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
+  }
+
   public final class SnapshotData implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.assist.AssistContent getAssistContent();
@@ -5024,13 +5075,18 @@
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onNotificationDirectReply(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+    method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
+    method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+    field public static final int SOURCE_FROM_APP = 0; // 0x0
+    field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
   }
 
   public final class NotificationStats implements android.os.Parcelable {
@@ -5184,6 +5240,16 @@
 
 }
 
+package android.service.sms {
+
+  public abstract class FinancialSmsService extends android.app.Service {
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.database.CursorWindow onGetSmsMessages(android.os.Bundle);
+    field public static final java.lang.String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+  }
+
+}
+
 package android.service.textclassifier {
 
   public abstract class TextClassifierService extends android.app.Service {
@@ -7023,9 +7089,9 @@
     method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
     method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, int[], int[]);
+    method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
@@ -7517,7 +7583,7 @@
     method public default void onMovedToDisplay(int, android.content.res.Configuration);
     method public abstract void onOverScrolled(int, int, boolean, boolean);
     method public default void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
-    method public default boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+    method public default void onProvideContentCaptureStructure(android.view.ViewStructure, int);
     method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
     method public abstract void onScrollChanged(int, int, int, int);
     method public abstract void onSizeChanged(int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c01cf1..738caec 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -985,6 +985,7 @@
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+    field public static final java.lang.String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = "autofill_smart_suggestion_emulation_flags";
     field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
     field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
     field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
@@ -1155,12 +1156,17 @@
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onNotificationDirectReply(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+    method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
+    method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+    field public static final int SOURCE_FROM_APP = 0; // 0x0
+    field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
   }
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -1318,9 +1324,9 @@
     method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
     method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, int[], int[]);
+    method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 5a6c813..7d675ce 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -18,7 +18,7 @@
     tidy: true,
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673       "-warnings-as-errors=*",
     ],
     srcs: [
         "libidmap2/BinaryStreamVisitor.cpp",
@@ -64,7 +64,7 @@
     tidy: true,
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673        "-warnings-as-errors=*",
     ],
     srcs: [
         "tests/BinaryStreamVisitorTests.cpp",
@@ -118,7 +118,7 @@
     tidy: true,
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673        "-warnings-as-errors=*",
     ],
     srcs: [
         "idmap2/Create.cpp",
@@ -165,7 +165,7 @@
     ],
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673        "-warnings-as-errors=*",
     ],
     srcs: [
         ":idmap2_aidl",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3ee0a06..e97d6c3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -205,6 +205,7 @@
         DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
         ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
         BatteryLevel battery_level = 10043;
+        BuildInformation build_information = 10044;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2421,8 +2422,8 @@
  */
 message CpuTimePerUid {
     optional int32 uid = 1 [(is_uid) = true];
-    optional uint64 user_time_millis = 2;
-    optional uint64 sys_time_millis = 3;
+    optional uint64 user_time_micros = 2;
+    optional uint64 sys_time_micros = 3;
 }
 
 /**
@@ -3331,6 +3332,40 @@
 }
 
 /**
+ * Pulls information about the device's build.
+ */
+message BuildInformation {
+    // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse.
+    // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags.
+    optional string fingerprint = 1;
+
+    // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated.
+    optional string brand = 2;
+
+    // Build.PRODUCT. The name of the overall product.
+    optional string product = 3;
+
+    // Build.DEVICE. The name of the industrial design.
+    optional string device = 4;
+
+    // Build.VERSION.RELEASE. The user-visible version string.  E.g., "1.0" or "3.4b5" or "bananas".
+    optional string version_release = 5;
+
+    // Build.ID. E.g. a label like "M4-rc20".
+    optional string id = 6;
+
+    // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to
+    // represent this build.
+    optional string version_incremental = 7;
+
+    // Build.TYPE. The type of build, like "user" or "eng".
+    optional string type = 8;
+
+    // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug".
+    optional string tags = 9;
+}
+
+/**
  * Pulls on-device BatteryStats power use calculations for the overall device.
  */
 message DeviceCalculatedPowerUse {
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ab635a0..87a065b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -265,6 +265,11 @@
          {{}, {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+        // BuildInformation.
+        {android::util::BUILD_INFORMATION,
+         {{}, {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 343709a..3157037 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -113,7 +113,7 @@
 
     // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
     // drops the metrics data in memory.
-    static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+    static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
 
     // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
     // data subscriber that it's time to call getData.
@@ -130,7 +130,7 @@
     static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
 
     /* Min period between two checks of byte size per config key in nanoseconds. */
-    static const int64_t kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC;
+    static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
 
     // Maximum age (30 days) that files on disk can exist in seconds.
     static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 2dcf50f..41c2e6c 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -41154,7 +41154,7 @@
 HSPLcom/android/internal/telephony/TimeServiceHelper;->setListener(Lcom/android/internal/telephony/TimeServiceHelper$Listener;)V
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;->toString()Ljava/lang/String;
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;->toString()Ljava/lang/String;
-HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
+HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByCountry(Ljava/lang/String;J)Lcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByNitzCountry(Lcom/android/internal/telephony/NitzData;Ljava/lang/String;)Lcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;
 HSPLcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastRangeForSubscriber(IIII)Z
@@ -51930,7 +51930,30 @@
 HSPLlibcore/reflect/Types;->getTypeArray(Llibcore/reflect/ListOfTypes;Z)[Ljava/lang/reflect/Type;
 HSPLlibcore/reflect/WildcardTypeImpl;->getLowerBounds()[Ljava/lang/reflect/Type;
 HSPLlibcore/reflect/WildcardTypeImpl;->getUpperBounds()[Ljava/lang/reflect/Type;
-HSPLlibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
+HSPLlibcore/timezone/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
+HSPLlibcore/timezone/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
+HSPLlibcore/timezone/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
+HSPLlibcore/timezone/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/timezone/CountryTimeZones$OffsetResult;
+HSPLlibcore/timezone/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
+HSPLlibcore/timezone/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
+HSPLlibcore/timezone/TimeZoneFinder;->getInstance()Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
+HSPLlibcore/timezone/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
+HSPLlibcore/timezone/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
+HSPLlibcore/timezone/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->processXml(Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)V
 HSPLlibcore/util/BasicLruCache;-><init>(I)V
 HSPLlibcore/util/BasicLruCache;->create(Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/BasicLruCache;->entryEvicted(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -51938,12 +51961,6 @@
 HSPLlibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/CollectionUtils;->removeDuplicates(Ljava/util/List;Ljava/util/Comparator;)V
-HSPLlibcore/util/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
-HSPLlibcore/util/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
-HSPLlibcore/util/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
-HSPLlibcore/util/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
-HSPLlibcore/util/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/util/CountryTimeZones$OffsetResult;
 HSPLlibcore/util/HexEncoding;->encode([BII)[C
 HSPLlibcore/util/NativeAllocationRegistry$CleanerRunner;->run()V
 HSPLlibcore/util/NativeAllocationRegistry$CleanerThunk;->run()V
@@ -51951,23 +51968,6 @@
 HSPLlibcore/util/NativeAllocationRegistry;->registerNativeAllocation(Ljava/lang/Object;J)Ljava/lang/Runnable;
 HSPLlibcore/util/SneakyThrow;->sneakyThrow(Ljava/lang/Throwable;)V
 HSPLlibcore/util/SneakyThrow;->sneakyThrow_(Ljava/lang/Throwable;)V
-HSPLlibcore/util/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
-HSPLlibcore/util/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/util/TimeZoneFinder$ReaderSupplier;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
-HSPLlibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
-HSPLlibcore/util/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
-HSPLlibcore/util/TimeZoneFinder;->getInstance()Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
-HSPLlibcore/util/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
-HSPLlibcore/util/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
-HSPLlibcore/util/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)Z
-HSPLlibcore/util/TimeZoneFinder;->processXml(Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)V
 HSPLlibcore/util/ZoneInfo$WallTime;-><init>()V
 HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsFromCalendar()V
 HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsToCalendar()V
@@ -63455,13 +63455,18 @@
 Llibcore/reflect/TypeVariableImpl;
 Llibcore/reflect/Types;
 Llibcore/reflect/WildcardTypeImpl;
-Llibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/CountryTimeZones$OffsetResult;
+Llibcore/timezone/CountryTimeZones$TimeZoneMapping;
+Llibcore/timezone/CountryTimeZones;
+Llibcore/timezone/TimeZoneDataFiles;
+Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+Llibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
+Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;
+Llibcore/timezone/TimeZoneFinder;
 Llibcore/util/BasicLruCache;
 Llibcore/util/CharsetUtils;
 Llibcore/util/CollectionUtils;
-Llibcore/util/CountryTimeZones$OffsetResult;
-Llibcore/util/CountryTimeZones$TimeZoneMapping;
-Llibcore/util/CountryTimeZones;
 Llibcore/util/EmptyArray;
 Llibcore/util/HexEncoding;
 Llibcore/util/NativeAllocationRegistry$CleanerRunner;
@@ -63469,11 +63474,6 @@
 Llibcore/util/NativeAllocationRegistry;
 Llibcore/util/Objects;
 Llibcore/util/SneakyThrow;
-Llibcore/util/TimeZoneDataFiles;
-Llibcore/util/TimeZoneFinder$ReaderSupplier;
-Llibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
-Llibcore/util/TimeZoneFinder$TimeZonesProcessor;
-Llibcore/util/TimeZoneFinder;
 Llibcore/util/ZoneInfo$CheckedArithmeticException;
 Llibcore/util/ZoneInfo$OffsetInterval;
 Llibcore/util/ZoneInfo$WallTime;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a770b9..f78506b 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -189,10 +189,10 @@
 Landroid/app/IUiModeManager;->disableCarMode(I)V
 Landroid/app/IUserSwitchObserver$Stub;-><init>()V
 Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
-Landroid/app/IWallpaperManager;->getHeightHint()I
+Landroid/app/IWallpaperManager;->getHeightHint(I)I
 Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
 Landroid/app/IWallpaperManager;->getWallpaperInfo(I)Landroid/app/WallpaperInfo;
-Landroid/app/IWallpaperManager;->getWidthHint()I
+Landroid/app/IWallpaperManager;->getWidthHint(I)I
 Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z
 Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V
 Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V
@@ -1488,86 +1488,10 @@
 Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
 Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
 Landroid/widget/ScrollBarDrawable;-><init>()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->clear()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->getRememberedPosition()I
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigit(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigitAndRememberPosition(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getDescriptionForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/util/Locale;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getInstance()Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;
-Lcom/android/i18n/phonenumbers/NumberParseException;->getErrorType()Lcom/android/i18n/phonenumbers/NumberParseException$ErrorType;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getDomesticCarrierCodeFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getFormat()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getLeadingDigitsPattern(I)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getNationalPrefixFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->leadingDigitsPatternSize()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getGeneralDesc()Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixForParsing()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixTransformRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getPreferredExtnPrefix()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasNationalPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasPreferredExtnPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->intlNumberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->numberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;->getMetadataList()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getNationalNumberPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_DEFAULT_COUNTRY:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITHOUT_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_IDD:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
 Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->clearCountryCode()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCodeSource()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getExtension()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getNationalNumber()J
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasCountryCode()Z
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasExtension()Z
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->end()I
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->number()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->rawString()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->start()I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;->POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->EXACT_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NOT_A_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NO_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->SHORT_NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
 Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->E164:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->INTERNATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->NATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->RFC3966:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
 Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE_OR_MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PAGER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PERSONAL_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PREMIUM_RATE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->SHARED_COST:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->TOLL_FREE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->UAN:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
 Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOICEMAIL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOIP:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->IS_POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->TOO_LONG:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->findNumbers(Ljava/lang/CharSequence;Ljava/lang/String;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;J)Ljava/lang/Iterable;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->format(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->formatInOriginalFormat(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getAsYouTypeFormatter(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getCountryCodeForRegion(Ljava/lang/String;)I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getInstance()Lcom/android/i18n/phonenumbers/PhoneNumberUtil;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNationalSignificantNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNumberType(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getRegionCodeForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isNumberMatch(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumberWithReason(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isValidNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
 Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
 Lcom/android/ims/ImsCall;->isMultiparty()Z
 Lcom/android/ims/ImsCall;->reject(I)V
@@ -4139,77 +4063,6 @@
 Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrollStateChanged(I)V
 Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageSelected(I)V
 Lcom/android/internal/widget/ViewPager;->getCurrentItem()I
-Lcom/android/okhttp/Connection;->getSocket()Ljava/net/Socket;
-Lcom/android/okhttp/ConnectionPool;->connections:Ljava/util/Deque;
-Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
-Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
-Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/HttpHandler;-><init>()V
-Lcom/android/okhttp/HttpsHandler;-><init>()V
-Lcom/android/okhttp/HttpUrl$Builder;->build()Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
-Lcom/android/okhttp/HttpUrl;->newBuilder()Lcom/android/okhttp/HttpUrl$Builder;
-Lcom/android/okhttp/HttpUrl;->parse(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HeaderParser;->skipUntil(Ljava/lang/String;ILjava/lang/String;)I
-Lcom/android/okhttp/internal/http/HeaderParser;->skipWhitespace(Ljava/lang/String;I)I
-Lcom/android/okhttp/internal/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Lcom/android/okhttp/internal/http/HttpEngine;->getConnection()Lcom/android/okhttp/Connection;
-Lcom/android/okhttp/internal/http/HttpEngine;->hasResponse()Z
-Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest:Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->readResponse()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sendRequest()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sentRequestMillis:J
-Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->writingRequestHeaders()V
-Lcom/android/okhttp/internal/http/RouteSelector;->hasNext()Z
-Lcom/android/okhttp/internal/huc/HttpsURLConnectionImpl;->delegate:Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->client:Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->httpEngine:Lcom/android/okhttp/internal/http/HttpEngine;
-Lcom/android/okhttp/internal/Internal;-><init>()V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->apply(Lcom/android/okhttp/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V
-Lcom/android/okhttp/internal/Internal;->callEngineGetStreamAllocation(Lcom/android/okhttp/Call;)Lcom/android/okhttp/internal/http/StreamAllocation;
-Lcom/android/okhttp/internal/Internal;->callEnqueue(Lcom/android/okhttp/Call;Lcom/android/okhttp/Callback;Z)V
-Lcom/android/okhttp/internal/Internal;->connectionBecameIdle(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)Z
-Lcom/android/okhttp/internal/Internal;->get(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/Address;Lcom/android/okhttp/internal/http/StreamAllocation;)Lcom/android/okhttp/internal/io/RealConnection;
-Lcom/android/okhttp/internal/Internal;->getHttpUrlChecked(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/internal/Internal;->instance:Lcom/android/okhttp/internal/Internal;
-Lcom/android/okhttp/internal/Internal;->internalCache(Lcom/android/okhttp/OkHttpClient;)Lcom/android/okhttp/internal/InternalCache;
-Lcom/android/okhttp/internal/Internal;->put(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)V
-Lcom/android/okhttp/internal/Internal;->routeDatabase(Lcom/android/okhttp/ConnectionPool;)Lcom/android/okhttp/internal/RouteDatabase;
-Lcom/android/okhttp/internal/Internal;->setCache(Lcom/android/okhttp/OkHttpClient;Lcom/android/okhttp/internal/InternalCache;)V
-Lcom/android/okhttp/internal/Platform;->get()Lcom/android/okhttp/internal/Platform;
-Lcom/android/okhttp/internal/Platform;->logW(Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Util;->closeAll(Ljava/io/Closeable;Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->closeQuietly(Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->EMPTY_BYTE_ARRAY:[B
-Lcom/android/okhttp/internal/Util;->UTF_8:Ljava/nio/charset/Charset;
-Lcom/android/okhttp/OkHttpClient;-><init>()V
-Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
-Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
-Lcom/android/okhttp/OkHttpClient;->getConnectionPool()Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->getCookieHandler()Ljava/net/CookieHandler;
-Lcom/android/okhttp/OkHttpClient;->getHostnameVerifier()Ljavax/net/ssl/HostnameVerifier;
-Lcom/android/okhttp/OkHttpClient;->getProxy()Ljava/net/Proxy;
-Lcom/android/okhttp/OkHttpClient;->getProxySelector()Ljava/net/ProxySelector;
-Lcom/android/okhttp/OkHttpClient;->getSslSocketFactory()Ljavax/net/ssl/SSLSocketFactory;
-Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
-Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Request;->method:Ljava/lang/String;
-Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/Response;->code:I
-Lcom/android/okhttp/Response;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Response;->message:Ljava/lang/String;
-Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
 Lcom/android/server/net/BaseNetworkObserver;-><init>()V
 Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
 Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 63a41ec..3069be6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1180,11 +1180,11 @@
             Manifest.permission.ACTIVITY_RECOGNITION,
             Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
             Manifest.permission.READ_MEDIA_AUDIO,
-            Manifest.permission.WRITE_MEDIA_AUDIO,
+            null, // no permission for OP_WRITE_MEDIA_AUDIO
             Manifest.permission.READ_MEDIA_VIDEO,
-            Manifest.permission.WRITE_MEDIA_VIDEO,
+            null, // no permission for OP_WRITE_MEDIA_VIDEO
             Manifest.permission.READ_MEDIA_IMAGES,
-            Manifest.permission.WRITE_MEDIA_IMAGES,
+            null, // no permission for OP_WRITE_MEDIA_IMAGES
     };
 
     /**
@@ -1462,11 +1462,11 @@
             AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
             AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
-            AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_AUDIO
+            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
-            AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_VIDEO
+            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
-            AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_IMAGES
+            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8bb704d..8a797dc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2046,8 +2046,6 @@
             StorageManager storage) {
         if (app.isInternal()) {
             return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
-        } else if (app.isExternalAsec()) {
-            return storage.getPrimaryPhysicalVolume();
         } else {
             return storage.findVolumeByUuid(app.volumeUuid);
         }
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5ef4be1..00547b4 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -87,24 +87,24 @@
 
     /**
      * Sets the dimension hint for the wallpaper. These hints indicate the desired
-     * minimum width and height for the wallpaper.
+     * minimum width and height for the wallpaper in a particular display.
      */
-    void setDimensionHints(in int width, in int height, in String callingPackage);
+    void setDimensionHints(in int width, in int height, in String callingPackage, int displayId);
 
     /**
-     * Returns the desired minimum width for the wallpaper.
+     * Returns the desired minimum width for the wallpaper in a particular display.
      */
-    int getWidthHint();
+    int getWidthHint(int displayId);
 
     /**
-     * Returns the desired minimum height for the wallpaper.
+     * Returns the desired minimum height for the wallpaper in a particular display.
      */
-    int getHeightHint();
+    int getHeightHint(int displayId);
 
     /**
      * Sets extra padding that we would like the wallpaper to have outside of the display.
      */
-    void setDisplayPadding(in Rect padding, in String callingPackage);
+    void setDisplayPadding(in Rect padding, in String callingPackage, int displayId);
 
     /**
      * Returns the name of the wallpaper. Private API.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f2a3e44..75b56f3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1338,6 +1338,11 @@
     private int mBadgeIcon = BADGE_ICON_NONE;
 
     /**
+     * Determines whether the platform can generate contextual actions for a notification.
+     */
+    private boolean mAllowSystemGeneratedContextualActions = true;
+
+    /**
      * Structure to encapsulate a named action that can be shown as part of this notification.
      * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
      * selected by the user.
@@ -2238,6 +2243,8 @@
         if (parcel.readInt() != 0) {
             mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
         }
+
+        mAllowSystemGeneratedContextualActions = parcel.readBoolean();
     }
 
     @Override
@@ -2353,6 +2360,7 @@
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
         that.mAppOverlayIntent = this.mAppOverlayIntent;
+        that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
 
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
@@ -2681,6 +2689,8 @@
             parcel.writeInt(0);
         }
 
+        parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+
         // mUsesStandardHeader is not written because it should be recomputed in listeners
     }
 
@@ -3101,6 +3111,10 @@
         return mAppOverlayIntent;
     }
 
+    public boolean getAllowSystemGeneratedContextualActions() {
+        return mAllowSystemGeneratedContextualActions;
+    }
+
     /**
      * The small icon representing this notification in the status bar and content view.
      *
@@ -5657,6 +5671,15 @@
         }
 
         /**
+         * Determines whether the platform can generate contextual actions for a notification.
+         * By default this is true.
+         */
+        public Builder setAllowSystemGeneratedContextualActions(boolean allowed) {
+            mN.mAllowSystemGeneratedContextualActions = allowed;
+            return this;
+        }
+
+        /**
          * @deprecated Use {@link #build()} instead.
          */
         @Deprecated
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index a2dae3b..0abc998 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -127,8 +127,8 @@
         if (obj instanceof Person) {
             final Person other = (Person) obj;
             return Objects.equals(mName, other.mName)
-                    && mIcon == null ? other.mIcon == null :
-                    (other.mIcon != null && mIcon.sameAs(other.mIcon))
+                    && (mIcon == null ? other.mIcon == null :
+                    (other.mIcon != null && mIcon.sameAs(other.mIcon)))
                     && Objects.equals(mUri, other.mUri)
                     && Objects.equals(mKey, other.mKey)
                     && mIsBot == other.mIsBot
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index e33d1fe..3ea3da2 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -36,6 +36,7 @@
 import android.util.AttributeSet;
 import android.util.Printer;
 import android.util.Xml;
+import android.view.SurfaceHolder;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -79,6 +80,7 @@
     final boolean mShowMetadataInPreview;
     final boolean mSupportsAmbientMode;
     final String mSettingsSliceUri;
+    final boolean mSupportMultipleDisplays;
 
     /**
      * Constructor.
@@ -143,6 +145,9 @@
                     false);
             mSettingsSliceUri = sa.getString(
                     com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
+            mSupportMultipleDisplays = sa.getBoolean(
+                    com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
+                    false);
 
             sa.recycle();
         } catch (NameNotFoundException e) {
@@ -163,6 +168,7 @@
         mShowMetadataInPreview = source.readInt() != 0;
         mSupportsAmbientMode = source.readInt() != 0;
         mSettingsSliceUri = source.readString();
+        mSupportMultipleDisplays = source.readInt() != 0;
         mService = ResolveInfo.CREATOR.createFromParcel(source);
     }
     
@@ -358,6 +364,19 @@
         return Uri.parse(mSettingsSliceUri);
     }
 
+    /**
+     * Returns whether this wallpaper service can support multiple engines to render on each surface
+     * independently. An example use case is a multi-display set-up where the wallpaper service can
+     * render surfaces to each of the connected displays.
+     *
+     * @see WallpaperService#onCreateEngine()
+     * @see WallpaperService.Engine#onCreate(SurfaceHolder)
+     * @return {@code true} if multiple engines can render independently on each surface.
+     */
+    public boolean supportsMultipleDisplays() {
+        return mSupportMultipleDisplays;
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "Service:");
         mService.dump(pw, prefix + "  ");
@@ -387,6 +406,7 @@
         dest.writeInt(mShowMetadataInPreview ? 1 : 0);
         dest.writeInt(mSupportsAmbientMode ? 1 : 0);
         dest.writeString(mSettingsSliceUri);
+        dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
         mService.writeToParcel(dest, flags);
     }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index bebe79e..27471ca 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1485,7 +1485,7 @@
             throw new RuntimeException(new DeadSystemException());
         }
         try {
-            return sGlobals.mService.getWidthHint();
+            return sGlobals.mService.getWidthHint(mContext.getDisplayId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1511,7 +1511,7 @@
             throw new RuntimeException(new DeadSystemException());
         }
         try {
-            return sGlobals.mService.getHeightHint();
+            return sGlobals.mService.getHeightHint(mContext.getDisplayId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1572,7 +1572,7 @@
                 throw new RuntimeException(new DeadSystemException());
             } else {
                 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight,
-                        mContext.getOpPackageName());
+                        mContext.getOpPackageName(), mContext.getDisplayId());
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1597,7 +1597,8 @@
                 Log.w(TAG, "WallpaperService not running");
                 throw new RuntimeException(new DeadSystemException());
             } else {
-                sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName());
+                sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(),
+                        mContext.getDisplayId());
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5514851..3f34803 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -193,10 +193,6 @@
     /** @hide */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
     /** @hide */
-    public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E;
-    /** @hide */
-    public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F;
-    /** @hide */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
 
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 75f5b17..e7f0053 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3589,6 +3589,27 @@
     public static final String
             ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
 
+
+    /**
+     * Activity Action: Perform text translation.
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
+
+    /**
+     * Activity Action: Define the meaning of the selected word(s).
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
     /**
      * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
      * exisiting sensor being disconnected.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4e110da..7c3b5e4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,7 +32,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.text.TextUtils;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
@@ -464,16 +463,6 @@
     public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
 
     /**
-     * Value for {@link #privateFlags}: Set to true if the application has been
-     * installed using the forward lock option.
-     *
-     * NOTE: DO NOT CHANGE THIS VALUE!  It is saved in packages.xml.
-     *
-     * {@hide}
-     */
-    public static final int PRIVATE_FLAG_FORWARD_LOCK = 1<<2;
-
-    /**
      * Value for {@link #privateFlags}: set to {@code true} if the application
      * is permitted to hold privileged permissions.
      *
@@ -651,7 +640,6 @@
             PRIVATE_FLAG_CANT_SAVE_STATE,
             PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
             PRIVATE_FLAG_DIRECT_BOOT_AWARE,
-            PRIVATE_FLAG_FORWARD_LOCK,
             PRIVATE_FLAG_HAS_DOMAIN_URLS,
             PRIVATE_FLAG_HIDDEN,
             PRIVATE_FLAG_INSTANT,
@@ -1843,17 +1831,6 @@
         return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
-    /** @hide */
-    public boolean isExternalAsec() {
-        return TextUtils.isEmpty(volumeUuid) && isExternal();
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean isForwardLocked() {
-        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
-    }
-
     /**
      * True if the application is installed as an instant app.
      * @hide
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f90199..07672d9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1374,12 +1374,6 @@
         }
 
         /** {@hide} */
-        public void setInstallFlagsInternal() {
-            installFlags |= PackageManager.INSTALL_INTERNAL;
-            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
-        }
-
-        /** {@hide} */
         @SystemApi
         public void setAllowDowngrade(boolean allowDowngrade) {
             if (allowDowngrade) {
@@ -1390,12 +1384,6 @@
         }
 
         /** {@hide} */
-        public void setInstallFlagsExternal() {
-            installFlags |= PackageManager.INSTALL_EXTERNAL;
-            installFlags &= ~PackageManager.INSTALL_INTERNAL;
-        }
-
-        /** {@hide} */
         public void setInstallFlagsForcePermissionPrompt() {
             installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ad06be3..a4b724b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -700,10 +700,8 @@
 
     /** @hide */
     @IntDef(flag = true, prefix = { "INSTALL_" }, value = {
-            INSTALL_FORWARD_LOCK,
             INSTALL_REPLACE_EXISTING,
             INSTALL_ALLOW_TEST,
-            INSTALL_EXTERNAL,
             INSTALL_INTERNAL,
             INSTALL_FROM_ADB,
             INSTALL_ALL_USERS,
@@ -721,17 +719,6 @@
     public @interface InstallFlags {}
 
     /**
-     * Flag parameter for {@link #installPackage} to indicate that this package
-     * should be installed as forward locked, i.e. only the app itself should
-     * have access to its code and non-resource assets.
-     *
-     * @deprecated new installs into ASEC containers are no longer supported.
-     * @hide
-     */
-    @Deprecated
-    public static final int INSTALL_FORWARD_LOCK = 0x00000001;
-
-    /**
      * Flag parameter for {@link #installPackage} to indicate that you want to
      * replace an already installed package, if one exists.
      *
@@ -750,17 +737,6 @@
 
     /**
      * Flag parameter for {@link #installPackage} to indicate that this package
-     * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
-     *
-     * @deprecated new installs into ASEC containers are no longer supported;
-     *             use adoptable storage instead.
-     * @hide
-     */
-    @Deprecated
-    public static final int INSTALL_EXTERNAL = 0x00000008;
-
-    /**
-     * Flag parameter for {@link #installPackage} to indicate that this package
      * must be installed to internal storage.
      *
      * @hide
@@ -1521,14 +1497,6 @@
 
     /**
      * Error code that is passed to the {@link IPackageMoveObserver} if the
-     * specified package cannot be moved since its forward locked.
-     *
-     * @hide
-     */
-    public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
-
-    /**
-     * Error code that is passed to the {@link IPackageMoveObserver} if the
      * specified package cannot be moved to the specified location.
      *
      * @hide
@@ -3143,33 +3111,6 @@
             @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
 
     /**
-     * Retrieve overall information about an application package that is
-     * installed on the system.
-     *
-     * @param packageName The full name (i.e. com.google.apps.contacts) of the
-     *            desired package.
-     * @param flags Additional option flags to modify the data returned.
-     * @param userHandle The user.
-     * @return A PackageInfo object containing information about the package. If
-     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package
-     *         is not found in the list of installed applications, the package
-     *         information is retrieved from the list of uninstalled
-     *         applications (which includes installed applications as well as
-     *         applications with data directory i.e. applications which had been
-     *         deleted with {@code DONT_DELETE_DATA} flag set).
-     * @throws NameNotFoundException if a package with the given name cannot be
-     *             found on the system.
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
-    @SystemApi
-    public @NonNull PackageInfo getPackageInfoAsUser(@NonNull String packageName,
-            @PackageInfoFlags int flags,
-            @NonNull UserHandle userHandle) throws NameNotFoundException {
-        return getPackageInfoAsUser(packageName, flags, userHandle.getIdentifier());
-    }
-
-    /**
      * Map from the current package names in use on the device to whatever
      * the current canonical name of that package is.
      * @param names Array of current names to be mapped.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2fcf1dd..c78960b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -829,9 +829,6 @@
 
     public static final int PARSE_MUST_BE_APK = 1 << 0;
     public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
-    /** @deprecated forward lock no longer functional. remove. */
-    @Deprecated
-    public static final int PARSE_FORWARD_LOCK = 1 << 2;
     public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
     public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
     public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
@@ -845,7 +842,6 @@
             PARSE_ENFORCE_CODE,
             PARSE_EXTERNAL_STORAGE,
             PARSE_FORCE_SDK,
-            PARSE_FORWARD_LOCK,
             PARSE_IGNORE_PROCESSES,
             PARSE_IS_SYSTEM_DIR,
             PARSE_MUST_BE_APK,
@@ -2006,11 +2002,6 @@
                 PARSE_DEFAULT_TARGET_SANDBOX);
         pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
 
-        /* Set the global "forward lock" flag */
-        if ((flags & PARSE_FORWARD_LOCK) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
-        }
-
         /* Set the global "on SD card" flag */
         if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -2532,55 +2523,33 @@
 
                 final ArraySet<String> newPermissions = new ArraySet<>();
                 newPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
-                newPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
                 newPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
-                newPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
                 newPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
-                newPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
                 newPermissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION);
                 newPermissions.add(android.Manifest.permission.WRITE_OBB);
 
-                final ArraySet<String> dangerousPermissions = new ArraySet<>();
-                dangerousPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
-                dangerousPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+                final ArraySet<String> removedPermissions = new ArraySet<>();
+                removedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
+                removedPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
 
                 for (int i = pkg.permissions.size() - 1; i >= 0; i--) {
                     final Permission p = pkg.permissions.get(i);
                     if (newPermissions.contains(p.info.name)) {
                         pkg.permissions.remove(i);
-                    } else if (dangerousPermissions.contains(p.info.name)) {
-                        p.info.protectionLevel &= ~PermissionInfo.PROTECTION_MASK_BASE;
-                        p.info.protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+                    } else if (removedPermissions.contains(p.info.name)) {
+                        p.info.flags &= ~PermissionInfo.FLAG_REMOVED;
                     }
                 }
             }
         } else {
             if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
                 pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
-                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
             }
             if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
                 pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
-                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
             }
             if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
                 pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
-                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
-            }
-
-            if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_LEGACY, false)) {
-                if (pkg.requestedPermissions
-                        .contains(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
-                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
-                }
-                if (pkg.requestedPermissions
-                        .contains(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
-                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
-                }
             }
         }
 
@@ -6801,7 +6770,7 @@
 
         /** @hide */
         public boolean isForwardLocked() {
-            return applicationInfo.isForwardLocked();
+            return false;
         }
 
         /** @hide */
@@ -6843,9 +6812,7 @@
         public boolean canHaveOatDir() {
             // The following app types CANNOT have oat directory
             // - non-updated system apps
-            // - forward-locked apps or apps installed in ASEC containers
-            return (!isSystem() || isUpdatedSystemApp())
-                    && !isForwardLocked() && !applicationInfo.isExternalAsec();
+            return !isSystem() || isUpdatedSystemApp();
         }
 
         public boolean isMatch(int flags) {
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 720c167..97d72f0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -360,6 +360,16 @@
     }
 
     /**
+     * Returns the number of binder proxies held in this process.
+     * @return number of binder proxies in this process
+     */
+    public static int getProxyCount() {
+        synchronized (sProxyMap) {
+            return sProxyMap.size();
+        }
+    }
+
+    /**
      * Dump proxy debug information.
      *
      * @hide
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8c5c415..900b62d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -54,7 +54,7 @@
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
-    private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+    private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 423ce77..b42f1c4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -137,8 +137,6 @@
     public static final String PROP_FORCE_VIDEO = "persist.fw.force_video";
     /** {@hide} */
     public static final String PROP_FORCE_IMAGES = "persist.fw.force_images";
-    /** {@hide} */
-    public static final String PROP_FORCE_LEGACY = "persist.fw.force_legacy";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d579f0f..dbdeb70 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9339,6 +9339,13 @@
             "location_background_throttle_package_whitelist";
 
         /**
+         * Whether to disable location status callbacks in preparation for deprecation.
+         * @hide
+         */
+        public static final String LOCATION_DISABLE_STATUS_CALLBACKS =
+                "location_disable_status_callbacks";
+
+        /**
          * Maximum staleness allowed for last location when returned to clients with only foreground
          * location permissions.
          * @hide
@@ -12701,6 +12708,17 @@
         public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
 
         /**
+         * Used to emulate Smart Suggestion for Augmented Autofill during development
+         *
+         * <p>Valid values: {@code 0x1} for IME and/or {@code 0x2} for popup window.
+         *
+         * @hide
+         */
+        @TestApi
+        public static final String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS =
+                "autofill_smart_suggestion_emulation_flags";
+
+        /**
          * Exemptions to the hidden API blacklist.
          *
          * @hide
diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java
new file mode 100644
index 0000000..af2da79
--- /dev/null
+++ b/core/java/android/service/intelligence/FillCallback.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Callback used to indicate at {@link FillRequest} has been fulfilled.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillCallback {
+
+    FillCallback() {}
+
+    /**
+     * Sets the response associated with the request.
+     *
+     * @param response response associated with the request, or {@code null} if the service
+     * could not provide autofill for the request.
+     */
+    public void onSuccess(@Nullable FillResponse response) {
+        final FillWindow fillWindow = response.getFillWindow();
+        if (fillWindow != null) {
+            fillWindow.show();
+        }
+        // TODO(b/111330312): properly implement on server-side by updating the Session state
+        // accordingly (and adding CTS tests)
+    }
+}
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java
new file mode 100644
index 0000000..c5e1242
--- /dev/null
+++ b/core/java/android/service/intelligence/FillController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Object used to interact with the autofill system.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillController {
+    private static final String TAG = "FillController";
+
+    private final AutofillProxy mProxy;
+
+    FillController(@NonNull AutofillProxy proxy) {
+        mProxy = proxy;
+    }
+
+    /**
+     * Fills the activity with the provided values.
+     *
+     * <p>As a side effect, the {@link FillWindow} associated with the {@link FillResponse} will be
+     * automatically {@link FillWindow#destroy() destroyed}.
+     */
+    public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
+        Preconditions.checkNotNull(values);
+
+        if (DEBUG) {
+            Log.d(TAG, "autofill() with " + values.size() + " values");
+        }
+
+        try {
+            mProxy.autofill(values);
+            final FillWindow fillWindow = mProxy.getFillWindow();
+            if (fillWindow != null) {
+                fillWindow.destroy();
+            }
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
new file mode 100644
index 0000000..95e9224
--- /dev/null
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.view.autofill.AutofillId;
+
+/**
+ * Represents a request to augment-fill an activity.
+ * @hide
+ */
+@SystemApi
+public final class FillRequest {
+
+    final AutofillProxy mProxy;
+
+    /** @hide */
+    FillRequest(@NonNull AutofillProxy proxy) {
+        mProxy = proxy;
+    }
+
+    /**
+     * Gets the session associated with this request.
+     */
+    @NonNull
+    public InteractionSessionId getSessionId() {
+        return mProxy.sessionId;
+    }
+
+    /**
+     * Gets the id of the field that triggered the request.
+     */
+    @NonNull
+    public AutofillId getFocusedId() {
+        return mProxy.focusedId;
+    }
+
+    /**
+     * Gets the Smart Suggestions object used to embed the autofill UI.
+     *
+     * @return object used to embed the autofill UI, or {@code null} if not supported.
+     */
+    @Nullable
+    public PresentationParams getPresentationParams() {
+        return mProxy.getSmartSuggestionParams();
+    }
+
+    @Override
+    public String toString() {
+        return "FillRequest[id=" + mProxy.focusedId + "]";
+    }
+}
diff --git a/core/java/android/service/intelligence/FillResponse.java b/core/java/android/service/intelligence/FillResponse.java
new file mode 100644
index 0000000..860c027
--- /dev/null
+++ b/core/java/android/service/intelligence/FillResponse.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.util.List;
+
+/**
+ * Response to a {@link FillRequest}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillResponse implements Parcelable {
+
+    private final FillWindow mFillWindow;
+
+    private FillResponse(@NonNull Builder builder) {
+        mFillWindow = builder.mFillWindow;
+    }
+
+    /** @hide */
+    @Nullable
+    FillWindow getFillWindow() {
+        return mFillWindow;
+    }
+
+    /**
+     * Builder for {@link FillResponse} objects.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static class Builder {
+
+        private FillWindow mFillWindow;
+
+        /**
+         * Sets the {@link FillWindow} used to display the Autofill UI.
+         *
+         * <p>Must be called when the service is handling the request so the Android System can
+         * properly synchronize the UI.
+         *
+         * @return this builder
+         */
+        public Builder setFillWindow(@NonNull FillWindow fillWindow) {
+            // TODO(b/111330312): implement / check not null / unit test
+            // TODO(b/111330312): throw exception if FillWindow not updated yet
+            mFillWindow = fillWindow;
+            return this;
+        }
+
+        /**
+         * Tells the Android System that the given {@code ids} should not trigger further
+         * {@link FillRequest requests} when focused.
+         *
+         * @param ids ids of the fields that should be ignored
+         *
+         * @return this builder
+         */
+        public Builder setIgnoredIds(@NonNull List<AutofillId> ids) {
+            // TODO(b/111330312): implement / check not null / unit test
+            return this;
+        }
+
+        /**
+         * Builds a new {@link FillResponse} instance.
+         *
+         * @throws IllegalStateException if any of the following conditions occur:
+         * <ol>
+         *   <li>{@link #build()} was already called.
+         *   <li>No call was made to {@link #setFillWindow(FillWindow)} or
+         *   {@ling #setIgnoredIds(List<AutofillId>)}.
+         * </ol>
+         *
+         * @return A built response.
+         */
+        public FillResponse build() {
+            // TODO(b/111330312): check conditions / add unit test
+            return new FillResponse(this);
+        }
+
+        // TODO(b/111330312): add methods to disable app / activity, either here or on manager
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        // TODO(b/111330312): implement
+    }
+
+    public static final Parcelable.Creator<FillResponse> CREATOR =
+            new Parcelable.Creator<FillResponse>() {
+
+                @Override
+                public FillResponse createFromParcel(Parcel parcel) {
+                    // TODO(b/111330312): implement
+                    return null;
+                }
+
+                @Override
+                public FillResponse[] newArray(int size) {
+                    return new FillResponse[size];
+                }
+    };
+}
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
new file mode 100644
index 0000000..4ea07bf
--- /dev/null
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Dialog;
+import android.graphics.Rect;
+import android.service.intelligence.PresentationParams.Area;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handle to a window used to display the augmented autofill UI.
+ *
+ * <p>The steps to create an augmented autofill UI are:
+ *
+ * <ol>
+ *   <li>Gets the {@link PresentationParams} from the {@link FillRequest}.
+ *   <li>Gets the {@link Area} to display the UI (for example, through
+ *   {@link PresentationParams#getSuggestionArea()}.
+ *   <li>Creates a {@link View} that must fit in the {@link Area#getBounds() area boundaries}.
+ *   <li>Set the proper listeners to the view (for example, a click listener that
+ *   triggers {@link FillController#autofill(java.util.List)}
+ *   <li>Call {@link #update(Area, View, long)} with these arguments.
+ *   <li>Create a {@link FillResponse} with the {@link FillWindow}.
+ *   <li>Pass such {@link FillResponse} to {@link FillCallback#onSuccess(FillResponse)}.
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillWindow {
+    private static final String TAG = "FillWindow";
+
+    /** Indicates the data being shown is a physical address */
+    public static final long FLAG_METADATA_ADDRESS = 0x1;
+
+    // TODO(b/111330312): add moar flags
+
+    /** @hide */
+    @LongDef(prefix = { "FLAG" }, value = {
+            FLAG_METADATA_ADDRESS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Flags{}
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private Dialog mDialog;
+
+    @GuardedBy("mLock")
+    private boolean mDestroyed;
+
+    /**
+     * Updates the content of the window.
+     *
+     * @param rootView new root view
+     * @param area coordinates to render the view.
+     * @param flags optional flags such as metadata of what will be rendered in the window. The
+     * Smart Suggestion host might decide whether or not to render the UI based on them.
+     *
+     * @return boolean whether the window was updated or not.
+     *
+     * @throws IllegalArgumentException if the area is not compatible with this window
+     */
+    public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) {
+        if (DEBUG) {
+            Log.d(TAG, "Updating " + area + " + with " + rootView);
+        }
+        // TODO(b/111330312): add test case for null
+        Preconditions.checkNotNull(area);
+        Preconditions.checkNotNull(rootView);
+        // TODO(b/111330312): must check the area is a valid object returned by
+        // SmartSuggestionParams, throw IAE if not
+
+        // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider
+
+
+        // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating
+        // the window underneath the existing view.
+
+        final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams();
+        if (smartSuggestion == null) {
+            Log.w(TAG, "No SmartSuggestionParams");
+            return false;
+        }
+
+        final Rect rect = area.getBounds();
+        if (rect == null) {
+            Log.wtf(TAG, "No Rect on SmartSuggestionParams");
+            return false;
+        }
+
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+
+            // TODO(b/111330312): once we have the SurfaceControl approach, we should update the
+            // window instead of destroying. In fact, it might be better to allocate a full window
+            // initially, which is transparent (and let touches get through) everywhere but in the
+            // rect boundaries.
+            destroy();
+
+            // TODO(b/111330312): make sure all touch events are handled, window is always closed,
+            // etc.
+
+            mDialog = new Dialog(rootView.getContext());
+            final Window window = mDialog.getWindow();
+            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+
+            final int height = rect.bottom - rect.top;
+            final int width = rect.right - rect.left;
+            final WindowManager.LayoutParams windowParams = window.getAttributes();
+            windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+            windowParams.y = rect.top - height;
+            windowParams.height = height;
+            windowParams.x = rect.left;
+            windowParams.width = width;
+
+            window.setAttributes(windowParams);
+            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+            final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
+            mDialog.setContentView(rootView, diagParams);
+
+            if (DEBUG) {
+                Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
+            }
+
+            area.proxy.setFillWindow(this);
+            return true;
+        }
+    }
+
+    /** @hide */
+    void show() {
+        // TODO(b/111330312): check if updated first / throw exception
+        if (DEBUG) Log.d(TAG, "show()");
+
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+            if (mDialog == null) {
+                throw new IllegalStateException("update() not called yet, or already destroyed()");
+            }
+
+            mDialog.show();
+        }
+    }
+
+    /**
+     * Destroys the window.
+     *
+     * <p>Once destroyed, this window cannot be used anymore
+     */
+    public void destroy() {
+        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed);
+
+        synchronized (this) {
+            if (mDestroyed) return;
+
+            if (mDialog != null) {
+                mDialog.dismiss();
+                mDialog = null;
+            }
+        }
+    }
+
+    private void checkNotDestroyedLocked() {
+        if (mDestroyed) {
+            throw new IllegalStateException("already destroyed()");
+        }
+    }
+
+    /** @hide */
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        synchronized (this) {
+            pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+            if (mDialog != null) {
+                pw.print(prefix); pw.print("dialog: ");
+                pw.println(mDialog.isShowing() ? "shown" : "hidden");
+                pw.print(prefix); pw.print("window: ");
+                pw.println(mDialog.getWindow().getAttributes());
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index 709c3b7..e2260d7 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -16,10 +16,12 @@
 
 package android.service.intelligence;
 
+import android.os.IBinder;
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.InteractionContext;
 import android.service.intelligence.SnapshotData;
 
+import android.view.autofill.AutofillId;
 import android.view.intelligence.ContentCaptureEvent;
 
 import java.util.List;
@@ -40,4 +42,9 @@
 
     void onActivitySnapshot(in InteractionSessionId sessionId,
                             in SnapshotData snapshotData);
+
+    void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient,
+                           int autofilSessionId, in AutofillId focusedId);
+
+    void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId);
 }
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
index 27569b6..040e25e 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -22,13 +22,26 @@
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.autofill.IAugmentedAutofillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -44,6 +57,9 @@
 
     private static final String TAG = "IntelligenceService";
 
+    // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+    static final boolean DEBUG = true;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
@@ -55,6 +71,8 @@
 
     private Handler mHandler;
 
+    private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies;
+
     private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
 
         @Override
@@ -87,6 +105,20 @@
                     obtainMessage(IntelligenceService::onActivitySnapshot,
                             IntelligenceService.this, sessionId, snapshotData));
         }
+
+        @Override
+        public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
+                int autofilSessionId, AutofillId focusedId) {
+            mHandler.sendMessage(obtainMessage(IntelligenceService::handleOnAutofillRequest,
+                    IntelligenceService.this, sessionId, client, autofilSessionId, focusedId));
+        }
+
+        @Override
+        public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest,
+                            IntelligenceService.this, sessionId));
+        }
     };
 
     @CallSuper
@@ -122,10 +154,93 @@
      * @param sessionId the session's Id
      * @param events the events
      */
-     // TODO(b/111276913): rename to onContentCaptureEvents
+    // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
+    // Request object so it can be extended
     public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
             @NonNull List<ContentCaptureEvent> events);
 
+    private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
+            @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
+        if (mAutofillProxies == null) {
+            mAutofillProxies = new ArrayMap<>();
+        }
+        AutofillProxy proxy = mAutofillProxies.get(sessionId);
+        if (proxy == null) {
+            proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId);
+            mAutofillProxies.put(sessionId,  proxy);
+        } else {
+            // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+            if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+        }
+        // TODO(b/111330312): set cancellation signal
+        final CancellationSignal cancellationSignal = null;
+        onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
+                new FillController(proxy), new FillCallback());
+    }
+
+    /**
+     * Asks the service to handle an "augmented" autofill request.
+     *
+     * <p>This method is called when the "stantard" autofill service cannot handle a request, which
+     * typically occurs when:
+     * <ul>
+     *   <li>Service does not recognize what should be autofilled.
+     *   <li>Service does not have data to fill the request.
+     *   <li>Service blacklisted that app (or activity) for autofill.
+     *   <li>App disabled itself for autofill.
+     * </ul>
+     *
+     * <p>Differently from the standard autofill workflow, on augmented autofill the service is
+     * responsible to generate the autofill UI and request the Android system to autofill the
+     * activity when the user taps an action in that UI (through the
+     * {@link FillController#autofill(List)} method).
+     *
+     * <p>The service <b>MUST</b> call {@link
+     * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible,
+     * passing {@code null} when it cannot fulfill the request.
+     *
+     * @param sessionId the session's id
+     * @param request the request to handle.
+     * @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 controller object used to interact with the autofill system.
+     * @param callback object used to notify the result of the request. Service <b>must</b> call
+     * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}.
+     */
+    public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
+            @NonNull FillCallback callback) {
+    }
+
+    private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+        AutofillProxy proxy = null;
+        if (mAutofillProxies != null) {
+            proxy = mAutofillProxies.get(sessionId);
+        }
+        if (proxy == null) {
+            // TODO(b/111330312): this might be fine, in which case we should logv it
+            Log.w(TAG, "No proxy for session " + sessionId);
+            return;
+        }
+        proxy.destroy();
+        mAutofillProxies.remove(sessionId);
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mAutofillProxies != null) {
+            final int size = mAutofillProxies.size();
+            pw.print("Number proxies: "); pw.println(size);
+            for (int i = 0; i < size; i++) {
+                final InteractionSessionId sessionId = mAutofillProxies.keyAt(i);
+                final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+                pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
+                proxy.dump("  ", pw);
+            }
+        }
+    }
+
     /**
      * Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a
      * session.
@@ -142,4 +257,99 @@
      * @param sessionId the id of the session to destroy
      */
     public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+
+    /** @hide */
+    static final class AutofillProxy {
+        private final Object mLock = new Object();
+        private final IAugmentedAutofillManagerClient mClient;
+        private final int mAutofillSessionId;
+        public final InteractionSessionId sessionId;
+        public final AutofillId focusedId;
+
+        @GuardedBy("mLock")
+        private SystemPopupPresentationParams mSmartSuggestion;
+
+        @GuardedBy("mLock")
+        private FillWindow mFillWindow;
+
+        private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
+                int autofillSessionId, @NonNull AutofillId focusedId) {
+            this.sessionId = sessionId;
+            mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
+            mAutofillSessionId = autofillSessionId;
+            this.focusedId = focusedId;
+            // TODO(b/111330312): linkToDeath
+        }
+
+        @NonNull
+        public SystemPopupPresentationParams getSmartSuggestionParams() {
+            synchronized (mLock) {
+                if (mSmartSuggestion != null) {
+                    return mSmartSuggestion;
+                }
+                Rect rect;
+                try {
+                    rect = mClient.getViewCoordinates(focusedId);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Could not get coordinates for " + focusedId);
+                    return null;
+                }
+                if (rect == null) {
+                    if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
+                    return null;
+                }
+                mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+                return mSmartSuggestion;
+            }
+        }
+
+        public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
+                throws RemoteException {
+            final int size = pairs.size();
+            final List<AutofillId> ids = new ArrayList<>(size);
+            final List<AutofillValue> values = new ArrayList<>(size);
+            for (int i = 0; i < size; i++) {
+                final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
+                ids.add(pair.first);
+                values.add(pair.second);
+            }
+            mClient.autofill(mAutofillSessionId, ids, values);
+        }
+
+        public void setFillWindow(@NonNull FillWindow fillWindow) {
+            synchronized (mLock) {
+                mFillWindow = fillWindow;
+            }
+        }
+
+        public FillWindow getFillWindow() {
+            synchronized (mLock) {
+                return mFillWindow;
+            }
+        }
+
+        public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+            pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId);
+            pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
+            pw.print(prefix); pw.print("client: "); pw.println(mClient);
+            final String prefix2 = prefix + "  ";
+            if (mFillWindow != null) {
+                pw.print(prefix); pw.println("window:");
+                mFillWindow.dump(prefix2, pw);
+            }
+            if (mSmartSuggestion != null) {
+                pw.print(prefix); pw.println("smartSuggestion:");
+                mSmartSuggestion.dump(prefix2, pw);
+            }
+        }
+
+        private void destroy() {
+            synchronized (mLock) {
+                if (mFillWindow != null) {
+                    if (DEBUG) Log.d(TAG, "destroying window");
+                    mFillWindow.destroy();
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java
new file mode 100644
index 0000000..c59069b
--- /dev/null
+++ b/core/java/android/service/intelligence/PresentationParams.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.DebugUtils;
+import android.view.View;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
+ * the intelligence service.
+ *
+ * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
+ *
+ * <ul>
+ *   <li>A small area associated with suggestions (like a small strip in the top of the IME),
+ *   returned by {@link #getSuggestionArea()}
+ *   <li>The full area (like the full IME window), returned by {@link #getFullArea()}
+ *   <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
+ * </ul>
+ *
+ * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
+ * dimensions the smart suggestion window, so the service can use it to calculate the size of the
+ * view that will be passed to {@link FillWindow#update(Area, View, long)}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class PresentationParams {
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the top of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the left of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the right of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted by the IME.
+     */
+    public static final int FLAG_HOST_IME = 0x10;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
+     * window.
+     */
+    public static final int FLAG_HOST_SYSTEM = 0x20;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_HINT_GRAVITY_TOP,
+            FLAG_HINT_GRAVITY_BOTTOM,
+            FLAG_HINT_GRAVITY_LEFT,
+            FLAG_HINT_GRAVITY_RIGHT,
+            FLAG_HOST_IME,
+            FLAG_HOST_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Flags {}
+
+
+    // /** @hide */
+    PresentationParams() {}
+
+    /**
+     * Gets the area of the suggestion strip for the given {@code metadata}
+     *
+     * @return strip dimensions, or {@code null} if the Smart Suggestion provider does not support
+     * suggestions strip.
+     */
+    @Nullable
+    public Area getSuggestionArea() {
+        return null;
+    }
+
+    /**
+     * Gets the full area for the of the Smart Suggestion provider.
+     *
+     * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
+     * embeding the UI on its full area.
+     */
+    @Nullable
+    public Area getFullArea() {
+        return null;
+    }
+
+    /**
+     * Gets flags associated with the Smart Suggestion.
+     *
+     * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
+     * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
+     * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
+     * {@link #FLAG_HOST_SYSTEM},
+     */
+    public @Flags int getFlags() {
+        return 0;
+    }
+
+    /** @hide */
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        final int flags = getFlags();
+        if (flags > 0) {
+            pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
+        }
+    }
+
+    private static String flagsToString(int flags) {
+        return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
+    }
+
+    /**
+     * Area associated with a {@link PresentationParams Smart Suggestions} provider.
+     *
+     * @hide
+     * */
+    @SystemApi
+    public abstract static class Area {
+
+        /** @hide */
+        public final AutofillProxy proxy;
+
+        private final Rect mBounds;
+
+        private Area(@NonNull AutofillProxy proxy, @NonNull Rect bounds) {
+            this.proxy = proxy;
+            mBounds = bounds;
+        }
+
+        /**
+         * Gets the area boundaries.
+         */
+        @NonNull
+        public Rect getBounds() {
+            return mBounds;
+        }
+
+        /**
+         * Gets a subarea limited by given boundaries.
+         *
+         * @param bounds boundaries relative to this Area.
+         *
+         * @throws {@link IllegalArgumentException} if the {@code bounds} is not fully-contained
+         * inside this full Area.
+         *
+         * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
+         * subaarea.
+         */
+        @Nullable
+        public Area getSubArea(@NonNull Rect bounds) {
+            // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return mBounds.toString();
+        }
+    }
+
+    /**
+     * System-provided poup window anchored to a view.
+     *
+     * <p>Used just for debugging purposes.
+     *
+     * @hide
+     */
+    public static final class SystemPopupPresentationParams extends PresentationParams {
+        private final Area mSuggestionArea;
+
+        public SystemPopupPresentationParams(@NonNull AutofillProxy proxy, @NonNull Rect rect) {
+            mSuggestionArea = new Area(proxy, rect) {};
+        }
+
+        @Override
+        public Area getSuggestionArea() {
+            return mSuggestionArea;
+        }
+
+        @Override
+        public int getFlags() {
+            return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
+        }
+
+        @Override
+        void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+            super.dump(prefix, pw);
+            pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
+        }
+    }
+}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d8bd002..ab94f43 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -47,4 +47,7 @@
     void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
     void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
     void onNotificationsSeen(in List<String> keys);
+    void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+    void onNotificationDirectReply(String key);
+    void onSuggestedReplySent(String key, in CharSequence reply, int source);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c1a3c2b..68da83f 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,6 +16,9 @@
 
 package android.service.notification;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -33,6 +36,7 @@
 
 import com.android.internal.os.SomeArgs;
 
+import java.lang.annotation.Retention;
 import java.util.List;
 
 /**
@@ -63,6 +67,13 @@
 public abstract class NotificationAssistantService extends NotificationListenerService {
     private static final String TAG = "NotificationAssistants";
 
+    /** @hide */
+    @Retention(SOURCE)
+    @IntDef({SOURCE_FROM_APP, SOURCE_FROM_ASSISTANT})
+    public @interface Source {}
+    public static final int SOURCE_FROM_APP = 0;
+    public static final int SOURCE_FROM_ASSISTANT = 1;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
@@ -160,6 +171,29 @@
     }
 
     /**
+     * Implement this to know when a notification is expanded / collapsed.
+     * @param key the notification key
+     * @param isUserAction whether the expanded change is caused by user action.
+     * @param isExpanded whether the notification is expanded.
+     */
+    public void onNotificationExpansionChanged(
+            String key, boolean isUserAction, boolean isExpanded) {}
+
+    /**
+     * Implement this to know when a direct reply is sent from a notification.
+     * @param key the notification key
+     */
+    public void onNotificationDirectReply(String key) {}
+
+    /**
+     * Implement this to know when a suggested reply is sent.
+     * @param key the notification key
+     * @param reply the reply that is just sent
+     * @param source the source of the reply, e.g. SOURCE_FROM_APP
+     */
+    public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
+
+    /**
      * Updates a notification.  N.B. this won’t cause
      * an existing notification to alert, but might allow a future update to
      * this notification to alert.
@@ -255,12 +289,43 @@
             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATIONS_SEEN,
                     args).sendToTarget();
         }
+
+        @Override
+        public void onNotificationExpansionChanged(String key, boolean isUserAction,
+                boolean isExpanded) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            args.argi1 = isUserAction ? 1 : 0;
+            args.argi2 = isExpanded ? 1 : 0;
+            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_EXPANSION_CHANGED, args)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onNotificationDirectReply(String key) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT, args)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            args.arg2 = reply;
+            args.argi2 = source;
+            mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget();
+        }
     }
 
     private final class MyHandler extends Handler {
         public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
         public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
         public static final int MSG_ON_NOTIFICATIONS_SEEN = 3;
+        public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
+        public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
+        public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -305,6 +370,31 @@
                     onNotificationsSeen(keys);
                     break;
                 }
+                case MSG_ON_NOTIFICATION_EXPANSION_CHANGED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    boolean isUserAction = args.argi1 == 1;
+                    boolean isExpanded = args.argi2 == 1;
+                    args.recycle();
+                    onNotificationExpansionChanged(key, isUserAction, isExpanded);
+                    break;
+                }
+                case MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    args.recycle();
+                    onNotificationDirectReply(key);
+                    break;
+                }
+                case MSG_ON_SUGGESTED_REPLY_SENT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    CharSequence reply = (CharSequence) args.arg2;
+                    int source = args.argi2;
+                    args.recycle();
+                    onSuggestedReplySent(key, reply, source);
+                    break;
+                }
             }
         }
     }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64eae0c..756a7c6 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,22 @@
         }
 
         @Override
+        public void onNotificationExpansionChanged(
+                String key, boolean isUserAction, boolean isExpanded) {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onNotificationDirectReply(String key) {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+            // no-op in the listener
+        }
+
+        @Override
         public void onNotificationChannelModification(String pkgName, UserHandle user,
                 NotificationChannel channel,
                 @ChannelOrGroupModificationTypes int modificationType) {
diff --git a/core/java/android/service/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
new file mode 100644
index 0000000..5fb7249
--- /dev/null
+++ b/core/java/android/service/sms/FinancialSmsService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.sms;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.database.CursorWindow;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * A service to support sms messages read for financial apps.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class FinancialSmsService extends Service {
+
+    private static final String TAG = "FinancialSmsService";
+
+    /**
+     * The {@link Intent} action that must be declared as handled by a service
+     * in its manifest for the system to recognize it as a quota providing
+     * service.
+     */
+    public static final String ACTION_FINANCIAL_SERVICE_INTENT =
+            "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+
+    /** {@hide} **/
+    public static final String EXTRA_SMS_MSGS = "sms_messages";
+
+    private FinancialSmsServiceWrapper mWrapper;
+
+    private void getSmsMessages(RemoteCallback callback, Bundle params) {
+        final Bundle data = new Bundle();
+        CursorWindow smsMessages = onGetSmsMessages(params);
+        if (smsMessages != null) {
+            data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
+        }
+        callback.sendResult(data);
+    }
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    /** @hide */
+    public FinancialSmsService() {
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWrapper = new FinancialSmsServiceWrapper();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mWrapper;
+    }
+
+    /**
+     * Get sms messages for financial apps.
+     *
+     * @param params parameters passed in by the calling app.
+     * @return the {@code CursorWindow} with all sms messages for the app to read.
+     *
+     * {@hide}
+     */
+    @Nullable
+    @SystemApi
+    public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
+
+    private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
+        @Override
+        public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
+            mHandler.sendMessage(obtainMessage(
+                    FinancialSmsService::getSmsMessages,
+                    FinancialSmsService.this,
+                    callback, params));
+        }
+    }
+
+}
diff --git a/core/java/android/service/sms/IFinancialSmsService.aidl b/core/java/android/service/sms/IFinancialSmsService.aidl
new file mode 100644
index 0000000..caabe58
--- /dev/null
+++ b/core/java/android/service/sms/IFinancialSmsService.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.sms;
+
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+/**
+ * Service used by financial apps to read sms messages.
+ *
+ * @hide
+ */
+oneway interface IFinancialSmsService
+{
+    void getSmsMessages(in RemoteCallback callback, in Bundle params);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6f51bec..f6bb762 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -129,7 +129,7 @@
         Bundle extras;
         boolean sync;
     }
-    
+
     /**
      * The actual implementation of a wallpaper.  A wallpaper service may
      * have multiple instances running (for example as a real wallpaper
@@ -144,7 +144,7 @@
         HandlerCaller mCaller;
         IWallpaperConnection mConnection;
         IBinder mWindowToken;
-        
+
         boolean mInitializing = true;
         boolean mVisible;
         boolean mReportedVisible;
@@ -208,7 +208,6 @@
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
 
-        DisplayManager mDisplayManager;
         Display mDisplay;
         private int mDisplayState;
 
@@ -419,7 +418,7 @@
         public int getDesiredMinimumHeight() {
             return mIWallpaperEngine.mReqHeight;
         }
-        
+
         /**
          * Return whether the wallpaper is currently visible to the user,
          * this is the last value supplied to
@@ -1015,14 +1014,6 @@
                 return;
             }
 
-            mDisplayManager = getSystemService(DisplayManager.class);
-            mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
-            if (mDisplay == null) {
-                // TODO(b/115486823) Ignore this engine.
-                Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
-                return;
-            }
-
             mIWallpaperEngine = wrapper;
             mCaller = wrapper.mCaller;
             mConnection = wrapper.mConnection;
@@ -1034,13 +1025,16 @@
             mWindow.setSession(mSession);
 
             mLayout.packageName = getPackageName();
-            mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
+            mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
+                    mCaller.getHandler());
+            mDisplay = mIWallpaperEngine.mDisplay;
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
             onCreate(mSurfaceHolder);
-            
+
             mInitializing = false;
+
             mReportedVisible = false;
             updateSurface(false, false, false);
         }
@@ -1202,8 +1196,8 @@
             
             mDestroyed = true;
 
-            if (mDisplayManager != null) {
-                mDisplayManager.unregisterDisplayListener(mDisplayListener);
+            if (mIWallpaperEngine.mDisplayManager != null) {
+                mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
             }
 
             if (mVisible) {
@@ -1272,7 +1266,9 @@
         int mReqWidth;
         int mReqHeight;
         final Rect mDisplayPadding = new Rect();
-        int mDisplayId;
+        final int mDisplayId;
+        final DisplayManager mDisplayManager;
+        final Display mDisplay;
 
         Engine mEngine;
 
@@ -1289,7 +1285,15 @@
             mReqHeight = reqHeight;
             mDisplayPadding.set(padding);
             mDisplayId = displayId;
-            
+
+            // Create a display context before onCreateEngine.
+            mDisplayManager = getSystemService(DisplayManager.class);
+            mDisplay = mDisplayManager.getDisplay(mDisplayId);
+
+            if (mDisplay == null) {
+                // Ignore this engine.
+                throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
+            }
             Message msg = mCaller.obtainMessage(DO_ATTACH);
             mCaller.sendMessage(msg);
         }
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index b970c25..85b6b88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -46,6 +46,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LineBackgroundSpan;
+import android.text.style.LineHeightSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
@@ -733,7 +734,9 @@
     /** @hide */
     public static final int LINE_BACKGROUND_SPAN = 27;
     /** @hide */
-    public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
+    public static final int LINE_HEIGHT_SPAN = 28;
+    /** @hide */
+    public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -928,6 +931,10 @@
                     readSpan(p, sp, new LineBackgroundSpan.Standard(p));
                     break;
 
+                case LINE_HEIGHT_SPAN:
+                    readSpan(p, sp, new LineHeightSpan.Standard(p));
+                    break;
+                    
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 2742ae0..a5d5af2 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -20,7 +20,10 @@
 import android.annotation.NonNull;
 import android.annotation.Px;
 import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
 import android.text.TextPaint;
+import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
@@ -71,7 +74,7 @@
      * covers only part of the paragraph.
      * </p>
      */
-    class Standard implements LineHeightSpan {
+    class Standard implements LineHeightSpan, ParcelableSpan {
 
         private final @Px int mHeight;
         /**
@@ -82,6 +85,48 @@
             mHeight = height;
         }
 
+        /**
+         * Constructor called from {@link TextUtils} to restore the span from a parcel
+         */
+        public Standard(Parcel src) {
+            mHeight = src.readInt();
+        }
+
+        /**
+         * Returns the line height specified by this span.
+         */
+        @Px
+        public int getHeight() {
+            return mHeight;
+        }
+
+        @Override
+        public int getSpanTypeId() {
+            return getSpanTypeIdInternal();
+        }
+
+        /** @hide */
+        @Override
+        public int getSpanTypeIdInternal() {
+            return TextUtils.LINE_HEIGHT_SPAN;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            writeToParcelInternal(dest, flags);
+        }
+
+        /** @hide */
+        @Override
+        public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mHeight);
+        }
+
         @Override
         public void chooseHeight(@NonNull CharSequence text, int start, int end,
                 int spanstartv, int lineHeight,
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f4dad62..eef7ea2 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -29,6 +29,7 @@
 import android.text.method.LinkMovementMethod;
 import android.text.method.MovementMethod;
 import android.text.style.URLSpan;
+import android.util.Log;
 import android.util.Patterns;
 import android.webkit.WebView;
 import android.widget.TextView;
@@ -72,6 +73,9 @@
  */
 
 public class Linkify {
+
+    private static final String LOG_TAG = "Linkify";
+
     /**
      *  Bit field indicating that web URLs should be matched in methods that
      *  take an options mask
@@ -310,6 +314,11 @@
      */
     private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
             @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+        if (text != null && containsUnsupportedCharacters(text.toString())) {
+            android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+            return false;
+        }
+
         if (mask == 0) {
             return false;
         }
@@ -356,6 +365,29 @@
     }
 
     /**
+     * Returns true if the specified text contains at least one unsupported character for applying
+     * links. Also logs the error.
+     *
+     * @param text the text to apply links to
+     * @hide
+     */
+    public static boolean containsUnsupportedCharacters(String text) {
+        if (text.contains("\u202C")) {
+            Log.e(LOG_TAG, "Unsupported character for applying links: u202C");
+            return true;
+        }
+        if (text.contains("\u202D")) {
+            Log.e(LOG_TAG, "Unsupported character for applying links: u202D");
+            return true;
+        }
+        if (text.contains("\u202E")) {
+            Log.e(LOG_TAG, "Unsupported character for applying links: u202E");
+            return true;
+        }
+        return false;
+    }
+
+    /**
      *  Scans the text of the provided TextView and turns all occurrences of
      *  the link types indicated in the mask into clickable links.  If matches
      *  are found the movement method for the TextView is set to
@@ -560,6 +592,11 @@
             @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
             @Nullable UrlSpanFactory urlSpanFactory) {
+        if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
+            android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+            return false;
+        }
+
         final String[] schemesCopy;
         if (defaultScheme == null) defaultScheme = "";
         if (schemes == null || schemes.length < 1) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f19d40a..0eaef5a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3373,9 +3373,8 @@
      *
      * |-------|-------|-------|-------|
      *                              1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
-     *                             1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT
-     *                            1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED
-     *                           1       PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE
+     *                             1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+     *                            1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
      * |-------|-------|-------|-------|
      */
 
@@ -3396,28 +3395,14 @@
      * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
      * should be called.
      *
-     * The idea is to call notifyNodeAdded() after the view is layout and visible, then call
-     * notifyNodeRemoved() when it's gone (without known when it was removed from the parent).
-     *
-     * TODO(b/111276913): the current algortighm could probably be optimized and some of them
-     * removed
+     * The idea is to call notifyAppeared() after the view is layout and visible, then call
+     * notifyDisappeared() when it's gone (without known when it was removed from the parent).
      */
-    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT = 0x10;
-    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED = 0x20;
-    private static final int PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE = 0x40;
+    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
+    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
 
     /* End of masks for mPrivateFlags4 */
 
-    private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED = 1;
-    private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED = 0;
-
-    @IntDef(flag = true, prefix = { "CONTENT_CAPTURE_NOTIFICATION_TYPE_" }, value = {
-            CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED,
-            CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ContentCaptureNotificationType {}
-
     /** @hide */
     protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
     /** @hide */
@@ -8152,6 +8137,9 @@
      * the user, and the activity rendering the view is enabled for Content Capture) is laid out and
      * is visible.
      *
+     * <p>The populated structure is then passed to the service through
+     * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}.
+     *
      * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
      * <ul>
      *   <li>{@link ViewStructure#setChildCount(int)}
@@ -8165,16 +8153,9 @@
      *   <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
      *   <li>{@link ViewStructure#setDataIsSensitive(boolean)}
      * </ul>
-     *
-     * @return whether the IntelligenceService should be notified that the view was added (through
-     * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view
-     * hierarchy. Most views should return {@code true} here, but views that contains virtual
-     * hierarchy might opt to return {@code false} and notify the manager independently, as the
-     * virtual views are rendered.
      */
-    public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+    public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
         onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
-        return true;
     }
 
     /** @hide */
@@ -8937,65 +8918,61 @@
      * Helper used to notify the {@link IntelligenceManager} when the view is removed or
      * added, based on whether it's laid out and visible, and without knowing if the parent removed
      * it from the view hierarchy.
+     *
+     * <p>This method is called from many places (visibility changed, view laid out, view attached
+     * or detached to/from window, etc...) and hence must contain the logic to call the manager, as
+     * described below:
+     *
+     * <ol>
+     *   <li>It should only be called when content capture is enabled for the view.
+     *   <li>It must call viewAppeared() before viewDisappeared()
+     *   <li>viewAppearead() can only be called when the view is visible and laidout
+     *   <li>It should not call the same event twice.
+     * </ol>
      */
-    // TODO(b/111276913): make sure the current algorithm covers all cases. For example, it should
-    // probably be called every time notifyEnterOrExitForAutoFillIfNeeded() is called as well.
-    private void notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
-            @ContentCaptureNotificationType int type) {
-        if (type != CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
-                && type != CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
-            // Sanity check so it does not screw up the flags
-            Log.wtf(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded(): "
-                    + "invalid type " + type + " for " + this);
-            return;
-        }
-
-        if (!isImportantForContentCapture()) return;
+    private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
 
         final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
         if (im == null || !im.isContentCaptureEnabled()) return;
 
-        // Make sure event is notified just once, and reset the
-        // PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE flag
-        boolean ignoreNotification = false;
-        if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
-            if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
-                    == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
-                ignoreNotification = true;
-            } else {
-                mPrivateFlags4 |= PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
-            }
-        } else {
-            if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
-                    == CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
-                ignoreNotification = true;
-            } else {
-                mPrivateFlags4 &= ~PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
-            }
-        }
-        if (ignoreNotification) {
-            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
-                // TODO(b/111276913): remove this log statement if the algorithm is not improved
-                // (right now it's called too many times when the activity is stopped and/or views
-                // disappear
-                Log.v(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded("
-                        + type + "): ignoring repeated notification on " + this);
-            }
-            return;
-        }
+        // NOTE: isImportantForContentCapture() is more expensive than im.isContentCaptureEnabled()
+        if (!isImportantForContentCapture()) return;
 
-        if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+        if (appeared) {
+            if (!isLaidOut() || !isVisibleToUser()
+                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+                            + isLaidOut() + ", visible=" + isVisibleToUser()
+                            + ": alreadyNotifiedAppeared="
+                            + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0));
+                }
+                return;
+            }
+            // All good: notify the manager...
             final ViewStructure structure = im.newViewStructure(this);
-            boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
-            if (notifyMgr) {
-                im.notifyViewAppeared(structure);
-            }
-            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
+            onProvideContentCaptureStructure(structure, /* flags= */ 0);
+            im.notifyViewAppeared(structure);
+            // ...and set the flags
+            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+            mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
         } else {
-            if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
-                return; // skip initial notification
+            if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
+                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this
+                            + ": notifiedAppeared="
+                            + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+                            + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+                                    & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+                }
+                return;
             }
+            // All good: notify the manager...
             im.notifyViewDisappeared(getAutofillId());
+            // ...and set the flags
+            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+            mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
         }
     }
 
@@ -12902,6 +12879,7 @@
     public void dispatchStartTemporaryDetach() {
         mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
         notifyEnterOrExitForAutoFillIfNeeded(false);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
         onStartTemporaryDetach();
     }
 
@@ -12928,6 +12906,7 @@
             notifyFocusChangeToInputMethodManager(true /* hasFocus */);
         }
         notifyEnterOrExitForAutoFillIfNeeded(true);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     /**
@@ -13509,9 +13488,8 @@
                         : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
             }
         }
-        notifyNodeAddedOrRemovedForContentCaptureIfNeeded(isVisible
-                ? CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
-                : CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
     }
 
     /**
@@ -19082,6 +19060,7 @@
         needGlobalAttributesUpdate(false);
 
         notifyEnterOrExitForAutoFillIfNeeded(true);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     @UnsupportedAppUsage
@@ -19131,8 +19110,7 @@
         }
 
         notifyEnterOrExitForAutoFillIfNeeded(false);
-        notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
-                CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
     }
 
     /**
@@ -21438,12 +21416,7 @@
             notifyEnterOrExitForAutoFillIfNeeded(true);
         }
 
-        if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
-                && (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT) == 0) {
-            notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
-                    CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED);
-            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT;
-        }
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     private boolean hasParentWantsFocus() {
@@ -23157,26 +23130,24 @@
      * Modifies the input matrix such that it maps view-local coordinates to
      * on-screen coordinates.
      *
-     * @param m input matrix to modify
-     * @hide
+     * @param matrix input matrix to modify
      */
-    @UnsupportedAppUsage
-    public void transformMatrixToGlobal(Matrix m) {
+    public void transformMatrixToGlobal(Matrix matrix) {
         final ViewParent parent = mParent;
         if (parent instanceof View) {
             final View vp = (View) parent;
-            vp.transformMatrixToGlobal(m);
-            m.preTranslate(-vp.mScrollX, -vp.mScrollY);
+            vp.transformMatrixToGlobal(matrix);
+            matrix.preTranslate(-vp.mScrollX, -vp.mScrollY);
         } else if (parent instanceof ViewRootImpl) {
             final ViewRootImpl vr = (ViewRootImpl) parent;
-            vr.transformMatrixToGlobal(m);
-            m.preTranslate(0, -vr.mCurScrollY);
+            vr.transformMatrixToGlobal(matrix);
+            matrix.preTranslate(0, -vr.mCurScrollY);
         }
 
-        m.preTranslate(mLeft, mTop);
+        matrix.preTranslate(mLeft, mTop);
 
         if (!hasIdentityMatrix()) {
-            m.preConcat(getMatrix());
+            matrix.preConcat(getMatrix());
         }
     }
 
@@ -23184,26 +23155,24 @@
      * Modifies the input matrix such that it maps on-screen coordinates to
      * view-local coordinates.
      *
-     * @param m input matrix to modify
-     * @hide
+     * @param matrix input matrix to modify
      */
-    @UnsupportedAppUsage
-    public void transformMatrixToLocal(Matrix m) {
+    public void transformMatrixToLocal(Matrix matrix) {
         final ViewParent parent = mParent;
         if (parent instanceof View) {
             final View vp = (View) parent;
-            vp.transformMatrixToLocal(m);
-            m.postTranslate(vp.mScrollX, vp.mScrollY);
+            vp.transformMatrixToLocal(matrix);
+            matrix.postTranslate(vp.mScrollX, vp.mScrollY);
         } else if (parent instanceof ViewRootImpl) {
             final ViewRootImpl vr = (ViewRootImpl) parent;
-            vr.transformMatrixToLocal(m);
-            m.postTranslate(0, vr.mCurScrollY);
+            vr.transformMatrixToLocal(matrix);
+            matrix.postTranslate(0, vr.mCurScrollY);
         }
 
-        m.postTranslate(-mLeft, -mTop);
+        matrix.postTranslate(-mLeft, -mTop);
 
         if (!hasIdentityMatrix()) {
-            m.postConcat(getInverseMatrix());
+            matrix.postConcat(getInverseMatrix());
         }
     }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d4c7069..9227249 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -202,6 +202,14 @@
     public static final String EXTRA_RESTORE_SESSION_TOKEN =
             "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
 
+    /**
+     * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
+            "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
+
     private static final String SESSION_ID_TAG = "android:sessionId";
     private static final String STATE_TAG = "android:state";
     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -370,6 +378,9 @@
     private Cleaner mServiceClientCleaner;
 
     @GuardedBy("mLock")
+    private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
+
+    @GuardedBy("mLock")
     private AutofillCallback mCallback;
 
     private final Context mContext;
@@ -1664,6 +1675,8 @@
                 final IAutoFillManager service = mService;
                 final IAutoFillManagerClient serviceClient = mServiceClient;
                 mServiceClientCleaner = Cleaner.create(this, () -> {
+                    // TODO(b/111330312): call service to also remove reference to
+                    // mAugmentedAutofillServiceClient
                     try {
                         service.removeClient(serviceClient, userId);
                     } catch (RemoteException e) {
@@ -1808,6 +1821,7 @@
             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
                 // Reset connection to system
                 mServiceClient = null;
+                mAugmentedAutofillServiceClient = null;
                 if (mServiceClientCleaner != null) {
                     mServiceClientCleaner.clean();
                     mServiceClientCleaner = null;
@@ -2054,6 +2068,29 @@
         }
     }
 
+    /**
+     * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
+     *
+     * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
+     * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
+     */
+    private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
+        synchronized (mLock) {
+            if (mAugmentedAutofillServiceClient == null) {
+                mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
+            }
+            final Bundle resultData = new Bundle();
+            resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
+                    mAugmentedAutofillServiceClient.asBinder());
+
+            try {
+                result.send(0, resultData);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
+            }
+        }
+    }
+
     /** @hide */
     public void requestHideFillUi() {
         requestHideFillUi(mIdShownFillUi, true);
@@ -2801,7 +2838,7 @@
     private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
         private final WeakReference<AutofillManager> mAfm;
 
-        AutofillManagerClient(AutofillManager autofillManager) {
+        private AutofillManagerClient(AutofillManager autofillManager) {
             mAfm = new WeakReference<>(autofillManager);
         }
 
@@ -2904,6 +2941,50 @@
                 afm.post(() -> afm.setSessionFinished(newState));
             }
         }
+
+        @Override
+        public void getAugmentedAutofillClient(IResultReceiver result) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.getAugmentedAutofillClient(result));
+            }
+        }
+    }
+
+    private static final class AugmentedAutofillManagerClient
+            extends IAugmentedAutofillManagerClient.Stub {
+        private final WeakReference<AutofillManager> mAfm;
+
+        private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
+            mAfm = new WeakReference<>(autofillManager);
+        }
+
+        @Override
+        public Rect getViewCoordinates(@NonNull AutofillId id) {
+            // TODO(b/111330312): use handler / callback?
+            final AutofillManager afm = mAfm.get();
+            if (afm == null) return null;
+
+            final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
+            // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
+            // fix (for example, take system status bar height into account) logic below
+            final int[] location = new int[2];
+            view.getLocationOnScreen(location);
+            final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
+                    location[1] + view.getHeight());
+            if (sVerbose) {
+                Log.v(TAG, "Coordinates for " + id + ": " + rect);
+            }
+            return rect;
+        }
+
+        @Override
+        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.autofill(sessionId, ids, values));
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
new file mode 100644
index 0000000..67cd0bf
--- /dev/null
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 java.util.List;
+
+import android.graphics.Rect;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Object running in the application process and responsible to provide the functionalities
+ * required by an Augmented Autofill service.
+ *
+ * @hide
+ */
+interface IAugmentedAutofillManagerClient {
+   Rect getViewCoordinates(in AutofillId id);
+   void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 0ff7a0b..63394b4 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -27,6 +27,8 @@
 import android.view.autofill.IAutofillWindowPresenter;
 import android.view.KeyEvent;
 
+import com.android.internal.os.IResultReceiver;
+
 /**
  * Object running in the application process and responsible for autofilling it.
  *
@@ -93,8 +95,18 @@
 
    /**
      * Marks the state of the session as finished.
+     *
      * @param newState STATE_FINISHED (because the autofill service returned a null
      * FillResponse) or STATE_UNKNOWN (because the session was removed).
      */
    void setSessionFinished(int newState);
+
+   /**
+    * Gets a reference to the binder object that can be used by the Augmented Autofill service.
+    *
+    * @param receiver, whose AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT extra will contain
+    * the reference.
+    */
+   void getAugmentedAutofillClient(in IResultReceiver result);
+
 }
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index dfa52d9..755c54c 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -391,7 +391,7 @@
     }
 
     /**
-     * Called by apps to explicitly enabled  or disable content capture.
+     * Called by apps to explicitly enable or disable content capture.
      *
      * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
index cc78e6b..ea57461 100644
--- a/core/java/android/view/intelligence/ViewNode.java
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -238,6 +238,8 @@
 
         @Override
         public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+            mNode.mText = text;
             // TODO(b/111276913): implement or move to superclass
         }
 
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
new file mode 100644
index 0000000..8df83c0
--- /dev/null
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 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.textclassifier;
+
+import android.annotation.NonNull;
+import android.app.Person;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class for action suggestions.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class ActionsSuggestionsHelper {
+    private static final int USER_LOCAL = 0;
+    private static final int FIRST_NON_LOCAL_USER = 1;
+
+    private ActionsSuggestionsHelper() {}
+
+    /**
+     * Converts the messages to a list of native messages object that the model can understand.
+     * <p>
+     * User id encoding - local user is represented as 0, Other users are numbered according to
+     * how far before they spoke last time in the conversation. For example, considering this
+     * conversation:
+     * <ul>
+     * <li> User A: xxx
+     * <li> Local user: yyy
+     * <li> User B: zzz
+     * </ul>
+     * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
+     */
+    @NonNull
+    public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
+            @NonNull List<ConversationActions.Message> messages) {
+        List<ConversationActions.Message> messagesWithText =
+                messages.stream()
+                        .filter(message -> !TextUtils.isEmpty(message.getText()))
+                        .collect(Collectors.toCollection(ArrayList::new));
+        if (messagesWithText.isEmpty()) {
+            return new ActionsSuggestionsModel.ConversationMessage[0];
+        }
+        int size = messagesWithText.size();
+        // If the last message (the most important one) does not have the Person object, we will
+        // just use the last message and consider this message is sent from a remote user.
+        ConversationActions.Message lastMessage = messages.get(size - 1);
+        boolean useLastMessageOnly = lastMessage.getAuthor() == null;
+        if (useLastMessageOnly) {
+            return new ActionsSuggestionsModel.ConversationMessage[]{
+                    new ActionsSuggestionsModel.ConversationMessage(
+                            FIRST_NON_LOCAL_USER,
+                            lastMessage.getText().toString())};
+        }
+
+        // Encode the messages in the reverse order, stop whenever the Person object is missing.
+        Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
+        PersonEncoder personEncoder = new PersonEncoder();
+        for (int i = size - 1; i >= 0; i--) {
+            ConversationActions.Message message = messagesWithText.get(i);
+            if (message.getAuthor() == null) {
+                break;
+            }
+            nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
+                    personEncoder.encode(message.getAuthor()),
+                    message.getText().toString()));
+        }
+        return nativeMessages.toArray(
+                new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
+    }
+
+    private static final class PersonEncoder {
+        private final Map<Person, Integer> mMapping = new ArrayMap<>();
+        private int mNextUserId = FIRST_NON_LOCAL_USER;
+
+        private int encode(Person person) {
+            if (ConversationActions.Message.PERSON_USER_LOCAL.equals(person)) {
+                return USER_LOCAL;
+            }
+            Integer result = mMapping.get(person);
+            if (result == null) {
+                mMapping.put(person, mNextUserId);
+                result = mNextUserId;
+                mNextUserId++;
+            }
+            return result;
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 5fcf227..1a7b911 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -345,6 +345,16 @@
 
     /** Represents a message in the conversation. */
     public static final class Message implements Parcelable {
+        /**
+         * Represents the local user.
+         *
+         * @see Builder#setAuthor(Person)
+         */
+        public static final Person PERSON_USER_LOCAL =
+                new Person.Builder()
+                        .setKey("text-classifier-conversation-actions-local-user")
+                        .build();
+
         @Nullable
         private final Person mAuthor;
         @Nullable
@@ -446,7 +456,11 @@
             @Nullable
             private Bundle mExtras;
 
-            /** Sets the person who composed this message. */
+            /**
+             * Sets the person who composed this message.
+             * <p>
+             * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+             */
             @NonNull
             public Builder setAuthor(@Nullable Person author) {
                 mAuthor = author;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f6c3d77..e0910c0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -105,7 +105,7 @@
     /**
      * @hide
      */
-    static final TextClassification EMPTY = new TextClassification.Builder().build();
+    public static final TextClassification EMPTY = new TextClassification.Builder().build();
 
     private static final String LOG_TAG = "TextClassification";
     // TODO(toki): investigate a way to derive this based on device properties.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 524f709..a2536cb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -90,6 +90,11 @@
     String TYPE_DATE_TIME = "datetime";
     /** Flight number in IATA format. */
     String TYPE_FLIGHT_NUMBER = "flight";
+    /**
+     * Word that users may be interested to look up for meaning.
+     * @hide
+     */
+    String TYPE_DICTIONARY = "dictionary";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -103,6 +108,7 @@
             TYPE_DATE,
             TYPE_DATE_TIME,
             TYPE_FLIGHT_NUMBER,
+            TYPE_DICTIONARY
     })
     @interface EntityType {}
 
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..8e14dfd 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,9 +40,9 @@
 import android.provider.Browser;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
-import android.text.TextUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
@@ -269,17 +269,17 @@
             final ZonedDateTime refTime = ZonedDateTime.now();
             final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
                     ? request.getEntityConfig().resolveEntityListModifications(
-                            getEntitiesForHints(request.getEntityConfig().getHints()))
+                    getEntitiesForHints(request.getEntityConfig().getHints()))
                     : mSettings.getEntityListDefault();
             final AnnotatorModel annotatorImpl =
                     getAnnotatorImpl(request.getDefaultLocales());
             final AnnotatorModel.AnnotatedSpan[] annotations =
                     annotatorImpl.annotate(
-                        textString,
-                        new AnnotatorModel.AnnotationOptions(
-                                refTime.toInstant().toEpochMilli(),
-                                        refTime.getZone().getId(),
-                                concatenateLocales(request.getDefaultLocales())));
+                            textString,
+                            new AnnotatorModel.AnnotationOptions(
+                                    refTime.toInstant().toEpochMilli(),
+                                    refTime.getZone().getId(),
+                                    concatenateLocales(request.getDefaultLocales())));
             for (AnnotatorModel.AnnotatedSpan span : annotations) {
                 final AnnotatorModel.ClassificationResult[] results =
                         span.getClassification();
@@ -373,20 +373,13 @@
                 // Actions model is optional, fallback if it is not available.
                 return mFallback.suggestConversationActions(request);
             }
-            List<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayList<>();
-            for (ConversationActions.Message message : request.getConversation()) {
-                if (TextUtils.isEmpty(message.getText())) {
-                    continue;
-                }
-                // TODO: We need to map the Person object to user id.
-                int userId = 1;
-                nativeMessages.add(
-                        new ActionsSuggestionsModel.ConversationMessage(
-                                userId, message.getText().toString()));
+            ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
+                    ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+            if (nativeMessages.length == 0) {
+                return mFallback.suggestConversationActions(request);
             }
             ActionsSuggestionsModel.Conversation nativeConversation =
-                    new ActionsSuggestionsModel.Conversation(nativeMessages.toArray(
-                            new ActionsSuggestionsModel.ConversationMessage[0]));
+                    new ActionsSuggestionsModel.Conversation(nativeMessages);
 
             ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
                     actionsImpl.suggestActions(nativeConversation, null);
@@ -523,10 +516,10 @@
         final TextClassification.Builder builder = new TextClassification.Builder()
                 .setText(classifiedText);
 
-        final int size = classifications.length;
+        final int typeCount = classifications.length;
         AnnotatorModel.ClassificationResult highestScoringResult =
-                size > 0 ? classifications[0] : null;
-        for (int i = 0; i < size; i++) {
+                typeCount > 0 ? classifications[0] : null;
+        for (int i = 0; i < typeCount; i++) {
             builder.setEntityType(classifications[i].getCollection(),
                                   classifications[i].getScore());
             if (classifications[i].getScore() > highestScoringResult.getScore()) {
@@ -534,9 +527,12 @@
             }
         }
 
+        // TODO: Make this configurable.
+        final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
         boolean isPrimaryAction = true;
         for (LabeledIntent labeledIntent : IntentFactory.create(
-                mContext, classifiedText, referenceTime, highestScoringResult)) {
+                mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold),
+                referenceTime, highestScoringResult)) {
             final RemoteAction action = labeledIntent.asRemoteAction(mContext);
             if (action == null) {
                 continue;
@@ -558,6 +554,42 @@
         return builder.setId(createId(text, start, end)).build();
     }
 
+    private boolean isForeignText(String text, float threshold) {
+        // TODO: Revisit this algorithm.
+        try {
+            final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
+            if (langResults.length <= 0) {
+                return false;
+            }
+
+            LangIdModel.LanguageResult highestScoringResult = langResults[0];
+            for (int i = 1; i < langResults.length; i++) {
+                if (langResults[i].getScore() > highestScoringResult.getScore()) {
+                    highestScoringResult = langResults[i];
+                }
+            }
+            if (highestScoringResult.getScore() < threshold) {
+                return false;
+            }
+            // TODO: Remove
+            Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
+                    highestScoringResult.getLanguage(), highestScoringResult.getScore()));
+
+            final Locale detected = new Locale(highestScoringResult.getLanguage());
+            final LocaleList deviceLocales = LocaleList.getDefault();
+            final int size = deviceLocales.size();
+            for (int i = 0; i < size; i++) {
+                if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (Throwable t) {
+            Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t);
+        }
+        return false;
+    }
+
     @Override
     public void dump(@NonNull IndentingPrintWriter printWriter) {
         synchronized (mLock) {
@@ -608,7 +640,8 @@
     /**
      * Helper class to store the information from which RemoteActions are built.
      */
-    private static final class LabeledIntent {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public static final class LabeledIntent {
 
         static final int DEFAULT_REQUEST_CODE = 0;
 
@@ -643,7 +676,8 @@
             return mDescription;
         }
 
-        Intent getIntent() {
+        @VisibleForTesting
+        public Intent getIntent() {
             return mIntent;
         }
 
@@ -687,7 +721,8 @@
     /**
      * Creates intents based on the classification type.
      */
-    static final class IntentFactory {
+    @VisibleForTesting
+    public static final class IntentFactory {
 
         private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
         private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
@@ -698,53 +733,70 @@
         public static List<LabeledIntent> create(
                 Context context,
                 String text,
+                boolean foreignText,
                 @Nullable Instant referenceTime,
                 @Nullable AnnotatorModel.ClassificationResult classification) {
             final String type = classification != null
                     ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
-                    : null;
+                    : "";
             text = text.trim();
+            final List<LabeledIntent> actions;
             switch (type) {
                 case TextClassifier.TYPE_EMAIL:
-                    return createForEmail(context, text);
+                    actions = createForEmail(context, text);
+                    break;
                 case TextClassifier.TYPE_PHONE:
-                    return createForPhone(context, text);
+                    actions = createForPhone(context, text);
+                    break;
                 case TextClassifier.TYPE_ADDRESS:
-                    return createForAddress(context, text);
+                    actions = createForAddress(context, text);
+                    break;
                 case TextClassifier.TYPE_URL:
-                    return createForUrl(context, text);
-                case TextClassifier.TYPE_DATE:
+                    actions = createForUrl(context, text);
+                    break;
+                case TextClassifier.TYPE_DATE:  // fall through
                 case TextClassifier.TYPE_DATE_TIME:
                     if (classification.getDatetimeResult() != null) {
                         final Instant parsedTime = Instant.ofEpochMilli(
                                 classification.getDatetimeResult().getTimeMsUtc());
-                        return createForDatetime(context, type, referenceTime, parsedTime);
+                        actions = createForDatetime(context, type, referenceTime, parsedTime);
                     } else {
-                        return new ArrayList<>();
+                        actions = new ArrayList<>();
                     }
+                    break;
                 case TextClassifier.TYPE_FLIGHT_NUMBER:
-                    return createForFlight(context, text);
+                    actions = createForFlight(context, text);
+                    break;
+                case TextClassifier.TYPE_DICTIONARY:
+                    actions = createForDictionary(context, text);
+                    break;
                 default:
-                    return new ArrayList<>();
+                    actions = new ArrayList<>();
+                    break;
             }
+            if (foreignText) {
+                insertTranslateAction(actions, context, text);
+            }
+            return actions;
         }
 
         @NonNull
         private static List<LabeledIntent> createForEmail(Context context, String text) {
-            return Arrays.asList(
-                    new LabeledIntent(
-                            context.getString(com.android.internal.R.string.email),
-                            context.getString(com.android.internal.R.string.email_desc),
-                            new Intent(Intent.ACTION_SENDTO)
-                                    .setData(Uri.parse(String.format("mailto:%s", text))),
-                            LabeledIntent.DEFAULT_REQUEST_CODE),
-                    new LabeledIntent(
-                            context.getString(com.android.internal.R.string.add_contact),
-                            context.getString(com.android.internal.R.string.add_contact_desc),
-                            new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                                    .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
-                            text.hashCode()));
+            final List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.email),
+                    context.getString(com.android.internal.R.string.email_desc),
+                    new Intent(Intent.ACTION_SENDTO)
+                            .setData(Uri.parse(String.format("mailto:%s", text))),
+                    LabeledIntent.DEFAULT_REQUEST_CODE));
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.add_contact),
+                    context.getString(com.android.internal.R.string.add_contact_desc),
+                    new Intent(Intent.ACTION_INSERT_OR_EDIT)
+                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+                            .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+                    text.hashCode()));
+            return actions;
         }
 
         @NonNull
@@ -801,12 +853,14 @@
             if (Uri.parse(text).getScheme() == null) {
                 text = "http://" + text;
             }
-            return Arrays.asList(new LabeledIntent(
+            final List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(new LabeledIntent(
                     context.getString(com.android.internal.R.string.browse),
                     context.getString(com.android.internal.R.string.browse_desc),
                     new Intent(Intent.ACTION_VIEW, Uri.parse(text))
                             .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
                     LabeledIntent.DEFAULT_REQUEST_CODE));
+            return actions;
         }
 
         @NonNull
@@ -828,12 +882,14 @@
 
         @NonNull
         private static List<LabeledIntent> createForFlight(Context context, String text) {
-            return Arrays.asList(new LabeledIntent(
+            final List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(new LabeledIntent(
                     context.getString(com.android.internal.R.string.view_flight),
                     context.getString(com.android.internal.R.string.view_flight_desc),
                     new Intent(Intent.ACTION_WEB_SEARCH)
                             .putExtra(SearchManager.QUERY, text),
                     text.hashCode()));
+            return actions;
         }
 
         @NonNull
@@ -864,5 +920,27 @@
                                     parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
                     parsedTime.hashCode());
         }
+
+        private static void insertTranslateAction(
+                List<LabeledIntent> actions, Context context, String text) {
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.translate),
+                    context.getString(com.android.internal.R.string.translate_desc),
+                    new Intent(Intent.ACTION_TRANSLATE)
+                            // TODO: Probably better to introduce a "translate" scheme instead of
+                            // using EXTRA_TEXT.
+                            .putExtra(Intent.EXTRA_TEXT, text),
+                    text.hashCode()));
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForDictionary(Context context, String text) {
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.define),
+                    context.getString(com.android.internal.R.string.define_desc),
+                    new Intent(Intent.ACTION_DEFINE)
+                            .putExtra(Intent.EXTRA_TEXT, text),
+                    text.hashCode()));
+        }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 02aee50..1e42c41 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -59,7 +59,7 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
-            STATUS_DIFFERENT_TEXT})
+            STATUS_DIFFERENT_TEXT, STATUS_UNSUPPORTED_CHARACTER})
     public @interface Status {}
 
     /** Links were successfully applied to the text. */
@@ -74,6 +74,9 @@
     /** The specified text does not match the text used to generate the links. */
     public static final int STATUS_DIFFERENT_TEXT = 3;
 
+    /** The specified text contains unsupported characters. */
+    public static final int STATUS_UNSUPPORTED_CHARACTER = 4;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index be4c3bc..8af4233 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -107,6 +107,13 @@
         Preconditions.checkNotNull(textLinks);
 
         final String textString = text.toString();
+
+        if (Linkify.containsUnsupportedCharacters(textString)) {
+            // Do not apply links to text containing unsupported characters.
+            android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+            return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
+        }
+
         if (!textString.startsWith(textLinks.getText())) {
             return TextLinks.STATUS_DIFFERENT_TEXT;
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1093719..414cb8f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -410,6 +410,9 @@
         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
+        if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+            setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+        }
 
         if (context == null) {
             throw new IllegalArgumentException("Invalid context argument");
@@ -2695,8 +2698,8 @@
     }
 
     @Override
-    public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
-        return mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
+    public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+        mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
     }
 
     @Override
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index ceada07..95e7a986 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -341,10 +341,9 @@
             return true; // true is the default value returned by View.isVisibleToUserForAutofill()
         }
 
-        default boolean onProvideContentCaptureStructure(
+        default void onProvideContentCaptureStructure(
                 @SuppressWarnings("unused") android.view.ViewStructure structure,
                 @SuppressWarnings("unused") int flags) {
-            return false; // WebView provides virtual views and is responsible to notify manager
         }
 
         public AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 12cc54d..c21182c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -106,9 +106,9 @@
     private boolean mHaveFrame = false;
     @UnsupportedAppUsage
     private boolean mAdjustViewBounds = false;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMaxWidth = Integer.MAX_VALUE;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMaxHeight = Integer.MAX_VALUE;
 
     // these are applied to the drawable
@@ -1331,9 +1331,17 @@
         }
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public void animateTransform(Matrix matrix) {
+    /**
+     * Applies a temporary transformation {@link Matrix} to the view's drawable when it is drawn.
+     * Allows custom scaling, translation, and perspective distortion during an animation.
+     *
+     * This method is a lightweight analogue of {@link ImageView#setImageMatrix(Matrix)} to use
+     * only during animations as this matrix will be cleared after the next drawable
+     * update or view's bounds change.
+     *
+     * @param matrix The transformation parameters in matrix form.
+     */
+    public void animateTransform(@Nullable Matrix matrix) {
         if (mDrawable == null) {
             return;
         }
@@ -1341,6 +1349,7 @@
             final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
             final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
             mDrawable.setBounds(0, 0, vwidth, vheight);
+            mDrawMatrix = null;
         } else {
             mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
             if (mDrawMatrix == null) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6cb0eaa..4caf288 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -31,6 +31,7 @@
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextUtils;
+import android.text.util.Linkify;
 import android.util.Log;
 import android.view.ActionMode;
 import android.view.textclassifier.SelectionEvent;
@@ -687,17 +688,6 @@
             mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
         }
 
-        @TextClassifier.WidgetType
-        private static String getWidetType(TextView textView) {
-            if (textView.isTextEditable()) {
-                return TextClassifier.WIDGET_TYPE_EDITTEXT;
-            }
-            if (textView.isTextSelectable()) {
-                return TextClassifier.WIDGET_TYPE_TEXTVIEW;
-            }
-            return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
-        }
-
         public void logSelectionStarted(
                 TextClassifier classificationSession,
                 CharSequence text, int index,
@@ -1045,7 +1035,12 @@
 
                 trimText();
                 final TextClassification classification;
-                if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+                if (Linkify.containsUnsupportedCharacters(mText)) {
+                    // Do not show smart actions for text containing unsupported characters.
+                    android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+                    classification = TextClassification.EMPTY;
+                } else if (mContext.getApplicationInfo().targetSdkVersion
+                        >= Build.VERSION_CODES.P) {
                     final TextClassification.Request request =
                             new TextClassification.Request.Builder(
                                     mTrimmedText, mRelativeStart, mRelativeEnd)
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index d55c09f..79dc670 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1419,27 +1419,10 @@
         return Switch.class.getName();
     }
 
+    /** @hide */
     @Override
-    public void onProvideStructure(ViewStructure structure) {
-        super.onProvideStructure(structure);
-        onProvideStructureForAssistOrAutofillOrViewCapture(structure);
-    }
-
-    @Override
-    public void onProvideAutofillStructure(ViewStructure structure, int flags) {
-        super.onProvideAutofillStructure(structure, flags);
-        onProvideStructureForAssistOrAutofillOrViewCapture(structure);
-    }
-
-    @Override
-    public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
-        final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
-        onProvideStructureForAssistOrAutofillOrViewCapture(structure);
-        return notifyManager;
-    }
-
-    // NOTE: currently there is no difference for any type, so it doesn't take flags
-    private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) {
+    protected void onProvideStructure(@NonNull ViewStructure structure,
+            @ViewStructureType int viewFor, int flags) {
         CharSequence switchText = isChecked() ? mTextOn : mTextOff;
         if (!TextUtils.isEmpty(switchText)) {
             CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35be766..4ed9924f0d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10942,6 +10942,9 @@
         if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
                 || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
             if (mLayout == null) {
+                if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
+                    Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
+                }
                 assumeLayout();
             }
             Layout layout = mLayout;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e2b8f7d..5d08a25 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -309,9 +309,6 @@
         } else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
             prefer = RECOMMEND_INSTALL_INTERNAL;
             checkBoth = false;
-        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            prefer = RECOMMEND_INSTALL_EXTERNAL;
-            checkBoth = false;
         } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
             prefer = RECOMMEND_INSTALL_INTERNAL;
             checkBoth = false;
diff --git a/core/java/com/android/internal/os/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
index 22435ae..b3aec0c 100644
--- a/core/java/com/android/internal/os/KernelCpuProcStringReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.nio.CharBuffer;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
@@ -59,6 +60,7 @@
     private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state";
     private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time";
     private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time";
+    private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat";
 
     private static final KernelCpuProcStringReader FREQ_TIME_READER =
             new KernelCpuProcStringReader(PROC_UID_FREQ_TIME);
@@ -66,19 +68,25 @@
             new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME);
     private static final KernelCpuProcStringReader CLUSTER_TIME_READER =
             new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME);
+    private static final KernelCpuProcStringReader USER_SYS_TIME_READER =
+            new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME);
 
-    public static KernelCpuProcStringReader getFreqTimeReaderInstance() {
+    static KernelCpuProcStringReader getFreqTimeReaderInstance() {
         return FREQ_TIME_READER;
     }
 
-    public static KernelCpuProcStringReader getActiveTimeReaderInstance() {
+    static KernelCpuProcStringReader getActiveTimeReaderInstance() {
         return ACTIVE_TIME_READER;
     }
 
-    public static KernelCpuProcStringReader getClusterTimeReaderInstance() {
+    static KernelCpuProcStringReader getClusterTimeReaderInstance() {
         return CLUSTER_TIME_READER;
     }
 
+    static KernelCpuProcStringReader getUserSysTimeReaderInstance() {
+        return USER_SYS_TIME_READER;
+    }
+
     private int mErrors = 0;
     private final Path mFile;
     private char[] mBuf;
@@ -164,12 +172,12 @@
             // ReentrantReadWriteLock allows lock downgrading.
             mReadLock.lock();
             return new ProcFileIterator(total);
-        } catch (FileNotFoundException e) {
+        } catch (FileNotFoundException | NoSuchFileException e) {
             mErrors++;
             Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile);
         } catch (IOException e) {
             mErrors++;
-            Slog.e(TAG, "Error reading: " + mFile, e);
+            Slog.e(TAG, "Error reading " + mFile, e);
         } finally {
             StrictMode.setThreadPolicyMask(oldMask);
             mWriteLock.unlock();
@@ -193,6 +201,11 @@
             mSize = size;
         }
 
+        /** @return Whether there are more lines in the iterator. */
+        public boolean hasNextLine() {
+            return mPos < mSize;
+        }
+
         /**
          * Fetches the next line. Note that all subsequent return values share the same char[]
          * under the hood.
@@ -214,44 +227,6 @@
             return CharBuffer.wrap(mBuf, start, i - start);
         }
 
-        /**
-         * Fetches the next line, converts all numbers into long, and puts into the given long[].
-         * To avoid GC, caller should try to use the same array for all calls. All non-numeric
-         * chars are treated as delimiters. All numbers are non-negative.
-         *
-         * @param array An array to store the parsed numbers.
-         * @return The number of elements written to the given array. -1 if there is no more line.
-         */
-        public int nextLineAsArray(long[] array) {
-            CharBuffer buf = nextLine();
-            if (buf == null) {
-                return -1;
-            }
-            int count = 0;
-            long num = -1;
-            char c;
-
-            while (buf.remaining() > 0 && count < array.length) {
-                c = buf.get();
-                if (num < 0) {
-                    if (isNumber(c)) {
-                        num = c - '0';
-                    }
-                } else {
-                    if (isNumber(c)) {
-                        num = num * 10 + c - '0';
-                    } else {
-                        array[count++] = num;
-                        num = -1;
-                    }
-                }
-            }
-            if (num >= 0) {
-                array[count++] = num;
-            }
-            return count;
-        }
-
         /** Total size of the proc file in chars. */
         public int size() {
             return mSize;
@@ -262,8 +237,63 @@
             mReadLock.unlock();
         }
 
-        private boolean isNumber(char c) {
-            return c >= '0' && c <= '9';
+
+    }
+
+    /**
+     * Converts all numbers in the CharBuffer into longs, and puts into the given long[].
+     *
+     * Space and colon are treated as delimiters. All other chars are not allowed. All numbers
+     * are non-negative. To avoid GC, caller should try to use the same array for all calls.
+     *
+     * This method also resets the given buffer to the original position before return so that
+     * it can be read again.
+     *
+     * @param buf   The char buffer to be converted.
+     * @param array An array to store the parsed numbers.
+     * @return The number of elements written to the given array. -1 if buf is null, -2 if buf
+     * contains invalid char, -3 if any number overflows.
+     */
+    public static int asLongs(CharBuffer buf, long[] array) {
+        if (buf == null) {
+            return -1;
         }
+        final int initialPos = buf.position();
+        int count = 0;
+        long num = -1;
+        char c;
+
+        while (buf.remaining() > 0 && count < array.length) {
+            c = buf.get();
+            if (!(isNumber(c) || c == ' ' || c == ':')) {
+                buf.position(initialPos);
+                return -2;
+            }
+            if (num < 0) {
+                if (isNumber(c)) {
+                    num = c - '0';
+                }
+            } else {
+                if (isNumber(c)) {
+                    num = num * 10 + c - '0';
+                    if (num < 0) {
+                        buf.position(initialPos);
+                        return -3;
+                    }
+                } else {
+                    array[count++] = num;
+                    num = -1;
+                }
+            }
+        }
+        if (num >= 0) {
+            array[count++] = num;
+        }
+        buf.position(initialPos);
+        return count;
+    }
+
+    private static boolean isNumber(char c) {
+        return c >= '0' && c <= '9';
     }
 }
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
new file mode 100644
index 0000000..7021b57
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Reads per-UID CPU time proc files. Concrete implementations are all nested inside.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} or {@link #readAbsolute} call
+ * within {@link #mMinTimeBetweenRead}. The throttler can be enable / disabled via a param in
+ * the constructor.
+ *
+ * This class and its subclasses are NOT thread-safe and NOT designed to be accessed by more than
+ * one caller since each caller has its own view of delta.
+ *
+ * @param <T> The type of CPU time for the callback.
+ */
+public abstract class KernelCpuUidTimeReader<T> {
+    protected static final boolean DEBUG = false;
+    private static final long DEFAULT_MIN_TIME_BETWEEN_READ = 1000L; // In milliseconds
+
+    final String mTag = this.getClass().getSimpleName();
+    final SparseArray<T> mLastTimes = new SparseArray<>();
+    final KernelCpuProcStringReader mReader;
+    final boolean mThrottle;
+    private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
+    private long mLastReadTimeMs = 0;
+
+    /**
+     * Callback interface for processing each line of the proc file.
+     *
+     * @param <T> The type of CPU time for the callback function.
+     */
+    public interface Callback<T> {
+        /**
+         * @param uid  UID of the app
+         * @param time Time spent. The exact data structure depends on subclass implementation.
+         */
+        void onUidCpuTime(int uid, T time);
+    }
+
+    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+        mReader = reader;
+        mThrottle = throttle;
+    }
+
+    /**
+     * Reads the proc file, calling into the callback with a delta of time for each UID.
+     *
+     * @param cb The callback to invoke for each line of the proc file. If null,the data is
+     *           consumed and subsequent calls to readDelta will provide a fresh delta.
+     */
+    public void readDelta(@Nullable Callback<T> cb) {
+        if (!mThrottle) {
+            readDeltaImpl(cb);
+            return;
+        }
+        final long currTimeMs = SystemClock.elapsedRealtime();
+        if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+            if (DEBUG) {
+                Slog.d(mTag, "Throttle readDelta");
+            }
+            return;
+        }
+        readDeltaImpl(cb);
+        mLastReadTimeMs = currTimeMs;
+    }
+
+    /**
+     * Reads the proc file, calling into the callback with cumulative time for each UID.
+     *
+     * @param cb The callback to invoke for each line of the proc file. It cannot be null.
+     */
+    public void readAbsolute(Callback<T> cb) {
+        if (!mThrottle) {
+            readAbsoluteImpl(cb);
+            return;
+        }
+        final long currTimeMs = SystemClock.elapsedRealtime();
+        if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+            if (DEBUG) {
+                Slog.d(mTag, "Throttle readAbsolute");
+            }
+            return;
+        }
+        readAbsoluteImpl(cb);
+        mLastReadTimeMs = currTimeMs;
+    }
+
+    abstract void readDeltaImpl(@Nullable Callback<T> cb);
+
+    abstract void readAbsoluteImpl(Callback<T> callback);
+
+    /**
+     * Removes the UID from internal accounting data. This method, overridden in
+     * {@link KernelCpuUidUserSysTimeReader}, also removes the UID from the kernel module.
+     *
+     * @param uid The UID to remove.
+     * @see KernelCpuUidUserSysTimeReader#removeUid(int)
+     */
+    public void removeUid(int uid) {
+        mLastTimes.delete(uid);
+    }
+
+    /**
+     * Removes UIDs in a given range from internal accounting data. This method, overridden in
+     * {@link KernelCpuUidUserSysTimeReader}, also removes the UIDs from the kernel module.
+     *
+     * @param startUid the first uid to remove.
+     * @param endUid   the last uid to remove.
+     * @see KernelCpuUidUserSysTimeReader#removeUidsInRange(int, int)
+     */
+    public void removeUidsInRange(int startUid, int endUid) {
+        if (endUid < startUid) {
+            Slog.e(mTag, "start UID " + startUid + " > end UID " + endUid);
+            return;
+        }
+        mLastTimes.put(startUid, null);
+        mLastTimes.put(endUid, null);
+        final int firstIndex = mLastTimes.indexOfKey(startUid);
+        final int lastIndex = mLastTimes.indexOfKey(endUid);
+        mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+    }
+
+    /**
+     * Set the minimum time in milliseconds between reads. If throttle is not enabled, this method
+     * has no effect.
+     *
+     * @param minTimeBetweenRead The minimum time in milliseconds.
+     */
+    public void setThrottle(long minTimeBetweenRead) {
+        if (mThrottle && minTimeBetweenRead >= 0) {
+            mMinTimeBetweenRead = minTimeBetweenRead;
+        }
+    }
+
+    /**
+     * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+     *
+     * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+     *
+     * This provides the time a UID's processes spent executing in user-space and kernel-space.
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+     * delta.
+     */
+    public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
+        private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
+
+        // [uid, user_time, system_time, (maybe) power_in_milli-amp-micro_seconds]
+        private final long[] mBuffer = new long[4];
+        // A reusable array to hold [user_time, system_time] for the callback.
+        private final long[] mUsrSysTime = new long[2];
+
+        public KernelCpuUidUserSysTimeReader(boolean throttle) {
+            super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+            super(reader, throttle);
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (iter == null) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) < 3) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    final int uid = (int) mBuffer[0];
+                    long[] lastTimes = mLastTimes.get(uid);
+                    if (lastTimes == null) {
+                        lastTimes = new long[2];
+                        mLastTimes.put(uid, lastTimes);
+                    }
+                    final long currUsrTimeUs = mBuffer[1];
+                    final long currSysTimeUs = mBuffer[2];
+                    mUsrSysTime[0] = currUsrTimeUs - lastTimes[0];
+                    mUsrSysTime[1] = currSysTimeUs - lastTimes[1];
+
+                    if (mUsrSysTime[0] < 0 || mUsrSysTime[1] < 0) {
+                        Slog.e(mTag, "Negative user/sys time delta for UID=" + uid
+                                + "\nPrev times: u=" + lastTimes[0] + " s=" + lastTimes[1]
+                                + " Curr times: u=" + currUsrTimeUs + " s=" + currSysTimeUs);
+                    } else if (mUsrSysTime[0] > 0 || mUsrSysTime[1] > 0) {
+                        if (cb != null) {
+                            cb.onUidCpuTime(uid, mUsrSysTime);
+                        }
+                    }
+                    lastTimes[0] = currUsrTimeUs;
+                    lastTimes[1] = currSysTimeUs;
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (iter == null) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) < 3) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    mUsrSysTime[0] = mBuffer[1]; // User time in microseconds
+                    mUsrSysTime[1] = mBuffer[2]; // System time in microseconds
+                    cb.onUidCpuTime((int) mBuffer[0], mUsrSysTime);
+                }
+            }
+        }
+
+        @Override
+        public void removeUid(int uid) {
+            super.removeUid(uid);
+            removeUidsFromKernelModule(uid, uid);
+        }
+
+        @Override
+        public void removeUidsInRange(int startUid, int endUid) {
+            super.removeUidsInRange(startUid, endUid);
+            removeUidsFromKernelModule(startUid, endUid);
+        }
+
+        /**
+         * Removes UIDs in a given range from the kernel module and internal accounting data. Only
+         * {@link BatteryStatsImpl} and its child processes should call this, as the change on
+         * Kernel is
+         * visible system wide.
+         *
+         * @param startUid the first uid to remove
+         * @param endUid   the last uid to remove
+         */
+        private void removeUidsFromKernelModule(int startUid, int endUid) {
+            Slog.d(mTag, "Removing uids " + startUid + "-" + endUid);
+            final int oldMask = StrictMode.allowThreadDiskWritesMask();
+            try (FileWriter writer = new FileWriter(REMOVE_UID_PROC_FILE)) {
+                writer.write(startUid + "-" + endUid);
+                writer.flush();
+            } catch (IOException e) {
+                Slog.e(mTag, "failed to remove uids " + startUid + " - " + endUid
+                        + " from uid_cputime module", e);
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
+            }
+        }
+    }
+
+    /**
+     * Reads /proc/uid_time_in_state which has the format:
+     *
+     * uid: [freq1] [freq2] [freq3] ...
+     * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+     * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+     * ...
+     *
+     * This provides the times a UID's processes spent executing at each different cpu frequency.
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+     * delta.
+     */
+    public static class KernelCpuUidFreqTimeReader extends KernelCpuUidTimeReader<long[]> {
+        private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+        // We check the existence of proc file a few times (just in case it is not ready yet when we
+        // start reading) and if it is not available, we simply ignore further read requests.
+        private static final int MAX_ERROR_COUNT = 5;
+
+        private final Path mProcFilePath;
+        private long[] mBuffer;
+        private long[] mCurTimes;
+        private long[] mDeltaTimes;
+        private long[] mCpuFreqs;
+
+        private int mFreqCount = 0;
+        private int mErrors = 0;
+        private boolean mPerClusterTimesAvailable;
+        private boolean mAllUidTimesAvailable = true;
+
+        public KernelCpuUidFreqTimeReader(boolean throttle) {
+            this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
+                    throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
+                boolean throttle) {
+            super(reader, throttle);
+            mProcFilePath = Paths.get(procFile);
+        }
+
+        /**
+         * @return Whether per-cluster times are available.
+         */
+        public boolean perClusterTimesAvailable() {
+            return mPerClusterTimesAvailable;
+        }
+
+        /**
+         * @return Whether all-UID times are available.
+         */
+        public boolean allUidTimesAvailable() {
+            return mAllUidTimesAvailable;
+        }
+
+        /**
+         * @return A map of all UIDs to their CPU time-in-state array in milliseconds.
+         */
+        public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
+            return mLastTimes;
+        }
+
+        /**
+         * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
+         * to determine if per-cluster times are available.
+         *
+         * @param powerProfile The PowerProfile to compare against.
+         * @return A long[] of CPU frequencies in Hz.
+         */
+        public long[] readFreqs(@NonNull PowerProfile powerProfile) {
+            checkNotNull(powerProfile);
+            if (mCpuFreqs != null) {
+                // No need to read cpu freqs more than once.
+                return mCpuFreqs;
+            }
+            if (!mAllUidTimesAvailable) {
+                return null;
+            }
+            final int oldMask = StrictMode.allowThreadDiskReadsMask();
+            try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+                if (readFreqs(reader.readLine()) == null) {
+                    return null;
+                }
+            } catch (IOException e) {
+                if (++mErrors >= MAX_ERROR_COUNT) {
+                    mAllUidTimesAvailable = false;
+                }
+                Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+                return null;
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
+            }
+            // Check if the freqs in the proc file correspond to per-cluster freqs.
+            final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
+            final int numClusters = powerProfile.getNumCpuClusters();
+            if (numClusterFreqs.size() == numClusters) {
+                mPerClusterTimesAvailable = true;
+                for (int i = 0; i < numClusters; ++i) {
+                    if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
+                        mPerClusterTimesAvailable = false;
+                        break;
+                    }
+                }
+            } else {
+                mPerClusterTimesAvailable = false;
+            }
+            Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
+            return mCpuFreqs;
+        }
+
+        private long[] readFreqs(String line) {
+            if (line == null) {
+                return null;
+            }
+            final String[] lineArray = line.split(" ");
+            if (lineArray.length <= 1) {
+                Slog.wtf(mTag, "Malformed freq line: " + line);
+                return null;
+            }
+            mFreqCount = lineArray.length - 1;
+            mCpuFreqs = new long[mFreqCount];
+            mCurTimes = new long[mFreqCount];
+            mDeltaTimes = new long[mFreqCount];
+            mBuffer = new long[mFreqCount + 1];
+            for (int i = 0; i < mFreqCount; ++i) {
+                mCpuFreqs[i] = Long.parseLong(lineArray[i + 1], 10);
+            }
+            return mCpuFreqs;
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    final int uid = (int) mBuffer[0];
+                    long[] lastTimes = mLastTimes.get(uid);
+                    if (lastTimes == null) {
+                        lastTimes = new long[mFreqCount];
+                        mLastTimes.put(uid, lastTimes);
+                    }
+                    copyToCurTimes();
+                    boolean notify = false;
+                    boolean valid = true;
+                    for (int i = 0; i < mFreqCount; i++) {
+                        // Unit is 10ms.
+                        mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+                        if (mDeltaTimes[i] < 0) {
+                            Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+                            valid = false;
+                        }
+                        notify |= mDeltaTimes[i] > 0;
+                    }
+                    if (notify && valid) {
+                        System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+                        if (cb != null) {
+                            cb.onUidCpuTime(uid, mDeltaTimes);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    copyToCurTimes();
+                    cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+                }
+            }
+        }
+
+        private void copyToCurTimes() {
+            for (int i = 0; i < mFreqCount; i++) {
+                mCurTimes[i] = mBuffer[i + 1] * 10;
+            }
+        }
+
+        private boolean checkPrecondition(ProcFileIterator iter) {
+            if (iter == null || !iter.hasNextLine()) {
+                // Error logged in KernelCpuProcStringReader.
+                return false;
+            }
+            CharBuffer line = iter.nextLine();
+            if (mCpuFreqs != null) {
+                return true;
+            }
+            return readFreqs(line.toString()) != null;
+        }
+
+        /**
+         * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
+         * read from the proc file.
+         *
+         * We need to assume that freqs in each cluster are strictly increasing.
+         * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
+         * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
+         *
+         * @return an IntArray filled with no. of freqs in each cluster.
+         */
+        private IntArray extractClusterInfoFromProcFileFreqs() {
+            final IntArray numClusterFreqs = new IntArray();
+            int freqsFound = 0;
+            for (int i = 0; i < mFreqCount; ++i) {
+                freqsFound++;
+                if (i + 1 == mFreqCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
+                    numClusterFreqs.add(freqsFound);
+                    freqsFound = 0;
+                }
+            }
+            return numClusterFreqs;
+        }
+    }
+
+    /**
+     * Reads /proc/uid_concurrent_active_time and reports CPU active time to BatteryStats to
+     * compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+     *
+     * /proc/uid_concurrent_active_time has the following format:
+     * cpus: n
+     * uid0: time0a, time0b, ..., time0n,
+     * uid1: time1a, time1b, ..., time1n,
+     * uid2: time2a, time2b, ..., time2n,
+     * ...
+     * where n is the total number of cpus (num_possible_cpus)
+     * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
+     *
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a
+     * proper delta.
+     */
+    public static class KernelCpuUidActiveTimeReader extends KernelCpuUidTimeReader<Long> {
+        private int mCores = 0;
+        private long[] mBuffer;
+
+        public KernelCpuUidActiveTimeReader(boolean throttle) {
+            super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+            super(reader, throttle);
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<Long> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    int uid = (int) mBuffer[0];
+                    long cpuActiveTime = sumActiveTime(mBuffer);
+                    if (cpuActiveTime > 0) {
+                        long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+                        if (delta > 0) {
+                            mLastTimes.put(uid, cpuActiveTime);
+                            if (cb != null) {
+                                cb.onUidCpuTime(uid, delta);
+                            }
+                        } else if (delta < 0) {
+                            Slog.e(mTag, "Negative delta from active time proc: " + delta);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<Long> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    long cpuActiveTime = sumActiveTime(mBuffer);
+                    if (cpuActiveTime > 0) {
+                        cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+                    }
+                }
+            }
+        }
+
+        private static long sumActiveTime(long[] times) {
+            // UID is stored at times[0].
+            double sum = 0;
+            for (int i = 1; i < times.length; i++) {
+                sum += (double) times[i] * 10 / i; // Unit is 10ms.
+            }
+            return (long) sum;
+        }
+
+        private boolean checkPrecondition(ProcFileIterator iter) {
+            if (iter == null || !iter.hasNextLine()) {
+                // Error logged in KernelCpuProcStringReader.
+                return false;
+            }
+            CharBuffer line = iter.nextLine();
+            if (mCores > 0) {
+                return true;
+            }
+
+            String str = line.toString();
+            if (!str.startsWith("cpus:")) {
+                Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+                return false;
+            }
+            int cores = Integer.parseInt(str.substring(5).trim(), 10);
+            if (cores <= 0) {
+                Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+                return false;
+            }
+            mCores = cores;
+            mBuffer = new long[mCores + 1]; // UID is stored at mBuffer[0].
+            return true;
+        }
+    }
+
+
+    /**
+     * Reads /proc/uid_concurrent_policy_time and reports CPU cluster times to BatteryStats to
+     * compute cluster power. See {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+     *
+     * /proc/uid_concurrent_policy_time has the following format:
+     * policyX: x policyY: y policyZ: z...
+     * uid1, time1a, time1b, ..., time1n,
+     * uid2, time2a, time2b, ..., time2n,
+     * ...
+     * The first line lists all policies (i.e. clusters) followed by # cores in each policy.
+     * Each uid is followed by x time entries corresponding to the time it spent on clusterX
+     * running concurrently with 0, 1, 2, ..., x - 1 other processes, then followed by y, z, ...
+     * time entries.
+     *
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a
+     * proper delta.
+     */
+    public static class KernelCpuUidClusterTimeReader extends KernelCpuUidTimeReader<long[]> {
+        private int mNumClusters;
+        private int mNumCores;
+        private int[] mCoresOnClusters; // # cores on each cluster.
+        private long[] mBuffer; // To store data returned from ProcFileIterator.
+        private long[] mCurTime;
+        private long[] mDeltaTime;
+
+        public KernelCpuUidClusterTimeReader(boolean throttle) {
+            super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+            super(reader, throttle);
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    int uid = (int) mBuffer[0];
+                    long[] lastTimes = mLastTimes.get(uid);
+                    if (lastTimes == null) {
+                        lastTimes = new long[mNumClusters];
+                        mLastTimes.put(uid, lastTimes);
+                    }
+                    sumClusterTime();
+                    boolean valid = true;
+                    boolean notify = false;
+                    for (int i = 0; i < mNumClusters; i++) {
+                        mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+                        if (mDeltaTime[i] < 0) {
+                            Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+                            valid = false;
+                        }
+                        notify |= mDeltaTime[i] > 0;
+                    }
+                    if (notify && valid) {
+                        System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+                        if (cb != null) {
+                            cb.onUidCpuTime(uid, mDeltaTime);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    sumClusterTime();
+                    cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+                }
+            }
+        }
+
+        private void sumClusterTime() {
+            // UID is stored at mBuffer[0].
+            int core = 1;
+            for (int i = 0; i < mNumClusters; i++) {
+                double sum = 0;
+                for (int j = 1; j <= mCoresOnClusters[i]; j++) {
+                    sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+                }
+                mCurTime[i] = (long) sum;
+            }
+        }
+
+        private boolean checkPrecondition(ProcFileIterator iter) {
+            if (iter == null || !iter.hasNextLine()) {
+                // Error logged in KernelCpuProcStringReader.
+                return false;
+            }
+            CharBuffer line = iter.nextLine();
+            if (mNumClusters > 0) {
+                return true;
+            }
+            // Parse # cores in clusters.
+            String[] lineArray = line.toString().split(" ");
+            if (lineArray.length % 2 != 0) {
+                Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+                return false;
+            }
+            int[] clusters = new int[lineArray.length / 2];
+            int cores = 0;
+            for (int i = 0; i < clusters.length; i++) {
+                if (!lineArray[i * 2].startsWith("policy")) {
+                    Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+                    return false;
+                }
+                clusters[i] = Integer.parseInt(lineArray[i * 2 + 1], 10);
+                cores += clusters[i];
+            }
+            mNumClusters = clusters.length;
+            mNumCores = cores;
+            mCoresOnClusters = clusters;
+            mBuffer = new long[cores + 1];
+            mCurTime = new long[mNumClusters];
+            mDeltaTime = new long[mNumClusters];
+            return true;
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9a7094a..69ba070 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -66,7 +66,7 @@
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
     void onNotificationDirectReplied(String key);
     void onNotificationSmartRepliesAdded(in String key, in int replyCount);
-    void onNotificationSmartReplySent(in String key, in int replyIndex);
+    void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int vis, int mask, String cause);
 
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 15745e9..8495850 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,6 +25,7 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.Process;
+import android.os.SystemProperties;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager.SplitPermissionInfo;
 import android.text.TextUtils;
@@ -930,6 +931,16 @@
                 XmlUtils.skipCurrentTag(parser);
             }
         }
+        // If the storage model feature flag is disabled, we need to fiddle
+        // around with permission definitions to return us to pre-Q behavior.
+        // STOPSHIP(b/112545973): remove once feature enabled by default
+        if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+            if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
+                    newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
+                    newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
+                return;
+            }
+        }
         if (!newPermissions.isEmpty()) {
             mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e064423..8c5b6f4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -794,7 +794,8 @@
     <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:label="@string/permlab_sdcardRead"
         android:description="@string/permdesc_sdcardRead"
-        android:protectionLevel="normal" />
+        android:protectionLevel="dangerous"
+        android:permissionFlags="removed" />
 
     <!-- Allows an application to write to external storage.
          <p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -814,7 +815,8 @@
     <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         android:label="@string/permlab_sdcardWrite"
         android:description="@string/permdesc_sdcardWrite"
-        android:protectionLevel="normal" />
+        android:protectionLevel="dangerous"
+        android:permissionFlags="removed" />
 
     <!-- Runtime permission controlling access to the user's shared aural media
          collection. -->
@@ -831,12 +833,6 @@
         android:description="@string/permdesc_audioRead"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to modify the user's shared audio collection. -->
-    <permission android:name="android.permission.WRITE_MEDIA_AUDIO"
-        android:label="@string/permlab_audioWrite"
-        android:description="@string/permdesc_audioWrite"
-        android:protectionLevel="dangerous" />
-
     <!-- Runtime permission controlling access to the user's shared visual media
          collection, including images and videos. -->
     <permission-group android:name="android.permission-group.MEDIA_VISUAL"
@@ -852,24 +848,12 @@
         android:description="@string/permdesc_imagesRead"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to modify the user's shared images collection. -->
-    <permission android:name="android.permission.WRITE_MEDIA_IMAGES"
-        android:label="@string/permlab_imagesWrite"
-        android:description="@string/permdesc_imagesWrite"
-        android:protectionLevel="dangerous" />
-
     <!-- Allows an application to read the user's shared video collection. -->
     <permission android:name="android.permission.READ_MEDIA_VIDEO"
         android:label="@string/permlab_videoRead"
         android:description="@string/permdesc_videoRead"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to modify the user's shared video collection. -->
-    <permission android:name="android.permission.WRITE_MEDIA_VIDEO"
-        android:label="@string/permlab_videoWrite"
-        android:description="@string/permdesc_videoWrite"
-        android:protectionLevel="dangerous" />
-
     <!-- Allows an application to access any geographic locations persisted in the
          user's shared collection. -->
     <permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
@@ -3024,6 +3008,13 @@
     <permission android:name="android.permission.BIND_TV_INPUT"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+         to ensure that only the system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi
          Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
          to ensure that only the system can bind to it.
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index 3c9f6ee..c024dbe 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -16,16 +16,14 @@
   -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+    style="@android:style/NotificationAction"
     android:id="@+id/action0"
     android:layout_width="wrap_content"
     android:layout_height="48dp"
     android:layout_gravity="center"
     android:gravity="start|center_vertical"
     android:layout_marginStart="4dp"
-    android:textColor="@color/notification_default_color"
     android:singleLine="true"
     android:textAlignment="viewStart"
     android:ellipsize="end"
-    android:background="@drawable/notification_material_action_background"
     />
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 07559f4..4258019 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -18,6 +18,7 @@
         android:id="@+id/actions_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/notification_action_list_margin_top"
         android:layout_gravity="bottom">
     <com.android.internal.widget.NotificationActionListLayout
             android:id="@+id/actions"
@@ -27,6 +28,7 @@
             android:orientation="horizontal"
             android:gravity="center_vertical"
             android:visibility="gone"
+            android:background="@color/notification_action_list_background_color"
             >
         <!-- actions will be added here -->
     </com.android.internal.widget.NotificationActionListLayout>
diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml
index 9fa7c6a..f165724 100644
--- a/core/res/res/layout/notification_material_action_tombstone.xml
+++ b/core/res/res/layout/notification_material_action_tombstone.xml
@@ -16,7 +16,7 @@
   -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+    style="@android:style/NotificationTombstoneAction"
     android:id="@+id/action0"
     android:layout_width="wrap_content"
     android:layout_height="48dp"
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 9de8842..a2ad3b9 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
         <!-- Color palette -->
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
-        <item name="colorAccent">@color/white</item>
+        <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
         <item name="colorControlNormal">?attr/textColorPrimary</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 605662a..6c4861b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7941,6 +7941,11 @@
         <!-- Uri that specifies a settings Slice for this wallpaper. -->
         <attr name="settingsSliceUri" />
 
+        <!-- Indicates that this wallpaper service can support multiple engines to render on each
+             surface independently. An example use case is a multi-display set-up where the
+             wallpaper service can render surfaces to each of the connected displays. -->
+        <attr name="supportsMultipleDisplays" format="boolean" />
+
     </declare-styleable>
 
     <!-- Use <code>dream</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4122cf0..16c0744 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -146,10 +146,14 @@
 
     <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
 
+    <color name="notification_action_button_text_color">@color/notification_default_color</color>
+
     <color name="notification_progress_background_color">@color/secondary_text_material_light</color>
 
     <color name="notification_action_list">#ffeeeeee</color>
 
+    <color name="notification_action_list_background_color">@null</color>
+
     <!-- Keyguard colors -->
     <color name="keyguard_avatar_frame_color">#ffffffff</color>
     <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e902989..f7b9961 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -200,6 +200,9 @@
     <!-- The height of the notification action list -->
     <dimen name="notification_action_list_height">60dp</dimen>
 
+    <!-- The margin of the notification action list at the top -->
+    <dimen name="notification_action_list_margin_top">0dp</dimen>
+
     <!-- The height of the notification action list -->
     <dimen name="notification_action_emphasized_height">48dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73dae08..3373d14 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2920,6 +2920,7 @@
         <public name="shell" />
         <public name="interactiveUiTimeout" />
         <public name="importantForContentCapture" />
+        <public name="supportsMultipleDisplays" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 200c35d..bd6d976 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1586,22 +1586,14 @@
     <string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardRead" product="nosdcard">read the contents of your USB storage</string>
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardRead" product="default">read the contents of your SD card</string>
+    <string name="permlab_sdcardRead">read the contents of your shared storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to read the contents of your USB storage.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardRead" product="default">Allows the app to read the contents of your SD card.</string>
+    <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardWrite" product="nosdcard">modify or delete the contents of your USB storage</string>
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardWrite" product="default">modify or delete the contents of your SD card</string>
+    <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardWrite" product="nosdcard">Allows the app to write to the USB storage.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
+    <string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_use_sip">make/receive SIP calls</string>
@@ -2989,6 +2981,18 @@
     <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
     <string name="view_flight_desc">Track selected flight</string>
 
+    <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
+    <string name="translate">Translate</string>
+    
+    <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
+    <string name="translate_desc">Translate selected text</string>
+
+    <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
+    <string name="define">Define</string>
+
+    <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
+    <string name="define_desc">Define selected text</string>
+
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
@@ -3867,10 +3871,8 @@
     <string name="action_mode_done">Done</string>
 
     <!-- Strings for MasterClearReceiver. -->
-    <!-- Text for progress dialog while erasing USB storage volume [CHAR LIMIT=NONE] -->
-    <string name="progress_erasing" product="nosdcard">Erasing USB storage\u2026</string>
-    <!-- Text for progress dialog while erasing SD card [CHAR LIMIT=NONE] -->
-    <string name="progress_erasing" product="default">Erasing SD card\u2026</string>
+    <!-- Text for progress dialog while erasing the shared storage volume [CHAR LIMIT=NONE] -->
+    <string name="progress_erasing">Erasing shared storage\u2026</string>
 
     <!-- Text for WebView's text selection Action Mode -->
     <!-- ActionBar action to share the current selection [CHAR LIMIT=10] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bd53936..18f7e48 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1526,10 +1526,22 @@
         <item name="gravity">top</item>
     </style>
 
-    <!-- Colored bordered ink button -->
+    <!-- The style for normal action button on notification -->
+    <style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
+      <item name="textColor">@color/notification_action_button_text_color</item>
+      <item name="background">@drawable/notification_material_action_background</item>
+    </style>
+
+    <!-- The style for emphasized action button on notification: Colored bordered ink button -->
     <style name="NotificationEmphasizedAction" parent="Widget.Material.Button">
         <item name="background">@drawable/btn_notification_emphasized</item>
         <item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item>
     </style>
 
+    <!-- The style for disabled action button on notification -->
+    <style name="NotificationTombstoneAction" parent="NotificationAction">
+      <item name="textColor">#555555</item>
+    </style>
+
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 82c9ff3..9264f90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,10 @@
   <java-symbol type="string" name="add_calendar_event_desc" />
   <java-symbol type="string" name="view_flight" />
   <java-symbol type="string" name="view_flight_desc" />
+  <java-symbol type="string" name="translate" />
+  <java-symbol type="string" name="translate_desc" />
+  <java-symbol type="string" name="define" />
+  <java-symbol type="string" name="define_desc" />
   <java-symbol type="string" name="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a317c99..8ac9451d 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -364,9 +364,7 @@
 
     private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
         // Flags explicitly over ride everything else.
-        if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            return INSTALL_LOC_SD;
-        } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+        if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
             return INSTALL_LOC_INT;
         }
         // Manifest option takes precedence next
@@ -437,8 +435,6 @@
 
             int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
             if (rLoc == INSTALL_LOC_INT) {
-                assertFalse(
-                        (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
                 assertEquals(appInstallPath, srcPath);
                 assertEquals(appInstallPath, publicSrcPath);
                 assertStartsWith("Native library should point to shared lib directory",
@@ -461,8 +457,6 @@
                     }
                 }
             } else if (rLoc == INSTALL_LOC_SD) {
-                assertFalse("The application should not be installed forward locked",
-                        (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
                 assertTrue("Application flags (" + info.flags
                         + ") should contain FLAG_EXTERNAL_STORAGE",
                         (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -845,31 +839,10 @@
     }
 
     @LargeTest
-    public void testReplaceFailSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
-    }
-
-    @LargeTest
     public void testReplaceNormalInternal() throws Exception {
         sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
     }
 
-    @LargeTest
-    public void testReplaceSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING
-                | PackageManager.INSTALL_EXTERNAL);
-    }
-
     /* -------------- Delete tests --- */
     private static class DeleteObserver extends IPackageDeleteObserver.Stub {
         private CountDownLatch mLatch = new CountDownLatch(1);
@@ -1015,31 +988,12 @@
         deleteFromRawResource(0, 0);
     }
 
-    @LargeTest
-    public void testDeleteSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
-    }
 
     @LargeTest
     public void testDeleteNormalInternalRetainData() throws Exception {
         deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
     }
 
-    @LargeTest
-    public void testDeleteSdcardRetainData() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
-    }
-
     void cleanUpInstall(InstallParams ip) throws Exception {
         if (ip == null) {
             return;
@@ -1104,60 +1058,6 @@
                 0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
-    /*
-     * Install a package on internal flash via PackageManager install flag. Replace
-     * the package via flag to install on sdcard. Make sure the new flag overrides
-     * the old install location.
-     */
-    @LargeTest
-    public void testReplaceFlagInternalSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = 0;
-        int rFlags = PackageManager.INSTALL_EXTERNAL;
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
-            assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    /*
-     * Install a package on sdcard via PackageManager install flag. Replace
-     * the package with no flags or manifest option and make sure the old
-     * install location is retained.
-     */
-    @LargeTest
-    public void testReplaceFlagSdcardInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = 0;
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
-            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
     @LargeTest
     public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
         // Do not run on devices with emulated external storage.
@@ -1375,34 +1275,6 @@
     }
 
     @LargeTest
-    public void testMoveAppExternalToExternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_EXTERNAL;
-        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
-        boolean fail = true;
-        int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
-    public void testMoveAppExternalToInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_EXTERNAL;
-        int moveFlags = PackageManager.MOVE_INTERNAL;
-        boolean fail = false;
-        int result = PackageManager.MOVE_SUCCEEDED;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
     public void testMoveAppFailInternalToExternalDelete() throws Exception {
         // Do not run on devices with emulated external storage.
         if (Environment.isExternalStorageEmulated()) {
@@ -1458,19 +1330,6 @@
     }
 
     /*
-     * Install an app on sdcard.
-     */
-    @LargeTest
-    public void testFlagE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
-    }
-
-    /*
      * Install an app with both internal and manifest option set.
      * should install on internal.
      */
@@ -1506,59 +1365,6 @@
                 false, -1,
                 PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
-    /*
-     * Install an app with both external and manifest option set.
-     * should install externally.
-     */
-    @LargeTest
-    public void testFlagEManifestI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    /*
-     * Install an app with both external and manifest preference for
-     * preferExternal. Should install externally.
-     */
-    @LargeTest
-    public void testFlagEManifestE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    /*
-     * Install an app with both external and manifest preference for
-     * auto. should install on external media.
-     */
-    @LargeTest
-    public void testFlagEManifestA() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
 
     /*
      * The following test functions verify install location for existing apps.
@@ -1586,75 +1392,6 @@
                 -1);
     }
 
-    @LargeTest
-    public void testFlagIExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    @LargeTest
-    public void testFlagEExistingI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    @LargeTest
-    public void testFlagEExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
     /*
      * The following set of tests verify the installation of apps with
      * install location attribute set to internalOnly, preferExternal and auto.
@@ -1720,29 +1457,6 @@
     }
 
     @LargeTest
-    public void testManifestIExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
     public void testManifestEExistingI() throws Exception {
         // Do not run on devices with emulated external storage.
         if (Environment.isExternalStorageEmulated()) {
@@ -1766,29 +1480,6 @@
     }
 
     @LargeTest
-    public void testManifestEExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
     public void testManifestAExistingI() throws Exception {
         int iFlags = PackageManager.INSTALL_INTERNAL;
         int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
@@ -1806,29 +1497,6 @@
                 PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
-    @LargeTest
-    public void testManifestAExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
     /*
      * The following set of tests check install location for existing
      * application based on user setting.
@@ -1896,42 +1564,6 @@
         setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
 
-    @LargeTest
-    public void testExistingEUserI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testExistingEUserE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testExistingEUserA() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
     /*
      * The following set of tests verify that the user setting defines
      * the install location.
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4802ebe..2c001c9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -124,6 +124,7 @@
                     Settings.Global.AUTOFILL_LOGGING_LEVEL,
                     Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
                     Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+                    Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,
                     Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
@@ -283,6 +284,7 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                    Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
                     Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
                     Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
new file mode 100644
index 0000000..f0faaf6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActionsSuggestionsHelperTest {
+    @Test
+    public void testToNativeMessages_emptyInput() {
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+
+        assertThat(conversationMessages).isEmpty();
+    }
+
+    @Test
+    public void testToNativeMessages_noTextMessages() {
+        ConversationActions.Message messageWithoutText =
+                new ConversationActions.Message.Builder().build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Collections.singletonList(messageWithoutText));
+
+        assertThat(conversationMessages).isEmpty();
+    }
+
+    @Test
+    public void testToNativeMessages_missingPersonInFirstMessage() {
+        ConversationActions.Message firstMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("first")
+                        .build();
+        ConversationActions.Message secondMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("second")
+                        .setAuthor(new Person.Builder().build())
+                        .build();
+        ConversationActions.Message thirdMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("third")
+                        .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+                        .build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Arrays.asList(firstMessage, secondMessage, thirdMessage));
+
+        assertThat(conversationMessages).hasLength(2);
+        assertNativeMessage(conversationMessages[0], secondMessage.getText(), 1);
+        assertNativeMessage(conversationMessages[1], thirdMessage.getText(), 0);
+    }
+
+    @Test
+    public void testToNativeMessages_missingPersonInMiddleOfConversation() {
+        ConversationActions.Message firstMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("first")
+                        .setAuthor(new Person.Builder().setName("first").build())
+                        .build();
+        ConversationActions.Message secondMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("second")
+                        .build();
+        ConversationActions.Message thirdMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("third")
+                        .setAuthor(new Person.Builder().setName("third").build())
+                        .build();
+        ConversationActions.Message fourthMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("fourth")
+                        .setAuthor(new Person.Builder().setName("fourth").build())
+                        .build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+        assertThat(conversationMessages).hasLength(2);
+        assertNativeMessage(conversationMessages[0], thirdMessage.getText(), 2);
+        assertNativeMessage(conversationMessages[1], fourthMessage.getText(), 1);
+    }
+
+    @Test
+    public void testToNativeMessages_userIdEncoding() {
+        Person userA = new Person.Builder().setName("userA").build();
+        Person userB = new Person.Builder().setName("userB").build();
+
+        ConversationActions.Message firstMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("first")
+                        .setAuthor(userB)
+                        .build();
+        ConversationActions.Message secondMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("second")
+                        .setAuthor(userA)
+                        .build();
+        ConversationActions.Message thirdMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("third")
+                        .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+                        .build();
+        ConversationActions.Message fourthMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("fourth")
+                        .setAuthor(userA)
+                        .build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+        assertThat(conversationMessages).hasLength(4);
+        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2);
+        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1);
+        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0);
+        assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1);
+    }
+
+    private static void assertNativeMessage(
+            ActionsSuggestionsModel.ConversationMessage nativeMessage,
+            CharSequence text,
+            int userId) {
+        assertThat(nativeMessage.getText()).isEqualTo(text.toString());
+        assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
new file mode 100644
index 0000000..0180856
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 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.textclassifier;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A builder used to build a fake context for testing.
+ */
+// TODO: Consider making public.
+final class FakeContextBuilder {
+
+    /**
+     * A component name that can be used for tests.
+     */
+    public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls");
+
+    private final PackageManager mPackageManager;
+    private final ContextWrapper mContext;
+    private final Map<String, ComponentName> mComponents = new HashMap<>();
+    private @Nullable ComponentName mAllIntentComponent;
+
+    FakeContextBuilder() {
+        mPackageManager = mock(PackageManager.class);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
+        mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
+            @Override
+            public PackageManager getPackageManager() {
+                return mPackageManager;
+            }
+        };
+    }
+
+    /**
+     * Sets the component name of an activity to handle the specified intent action.
+     * <p>
+     * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+     */
+    public FakeContextBuilder setIntentComponent(
+            String intentAction, @Nullable ComponentName component) {
+        Preconditions.checkNotNull(intentAction);
+        mComponents.put(intentAction, component);
+        return this;
+    }
+
+
+    /**
+     * Sets the component name of an activity to handle all intents.
+     * <p>
+     * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+     */
+    public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) {
+        mAllIntentComponent = component;
+        return this;
+    }
+
+    /**
+     * Builds and returns a fake context.
+     */
+    public Context build() {
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenAnswer(
+                (Answer<ResolveInfo>) invocation -> {
+                    final String action = ((Intent) invocation.getArgument(0)).getAction();
+                    final ComponentName component = mComponents.containsKey(action)
+                            ? mComponents.get(action)
+                            : mAllIntentComponent;
+                    return getResolveInfo(component);
+                });
+        return mContext;
+    }
+
+    /**
+     * Returns a component name with random package and class names.
+     */
+    public static ComponentName newComponent() {
+        return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+    }
+
+    private static ResolveInfo getResolveInfo(ComponentName component) {
+        final ResolveInfo info;
+        if (component == null) {
+            info = null;
+        } else {
+            // NOTE: If something breaks in TextClassifier because we expect more fields to be set
+            // in here, just add them.
+            info = new ResolveInfo();
+            info.activityInfo = new ActivityInfo();
+            info.activityInfo.packageName = component.getPackageName();
+            info.activityInfo.name = component.getClassName();
+            info.activityInfo.exported = true;
+            info.activityInfo.applicationInfo = new ApplicationInfo();
+            info.activityInfo.applicationInfo.icon = 0;
+        }
+        return info;
+    }
+
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
new file mode 100644
index 0000000..bae2be3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.AnnotatorModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentFactoryTest {
+
+    private static final String TEXT = "text";
+
+    @Test
+    public void create_typeDictionary() {
+        AnnotatorModel.ClassificationResult classificationResult =
+                new AnnotatorModel.ClassificationResult(
+                        TextClassifier.TYPE_DICTIONARY,
+                        1.0f,
+                        null,
+                        null);
+
+        List<TextClassifierImpl.LabeledIntent> intents = TextClassifierImpl.IntentFactory.create(
+                InstrumentationRegistry.getContext(),
+                TEXT,
+                false,
+                null,
+                classificationResult);
+
+        assertThat(intents).hasSize(1);
+        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
+        Intent intent = labeledIntent.getIntent();
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+        assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 46aa5b4..a3f69d9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -20,18 +20,10 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
 import android.os.LocaleList;
 import android.service.textclassifier.TextClassifierService;
 import android.support.test.InstrumentationRegistry;
@@ -41,7 +33,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -78,23 +69,10 @@
 
     @Test
     public void testCannotResolveIntent() {
-        final PackageManager fakePackageMgr = mock(PackageManager.class);
-
-        ResolveInfo validInfo = mContext.getPackageManager().resolveActivity(
-                new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0);
-        // Make packageManager fail when it gets the following intent:
-        ArgumentMatcher<Intent> toFailIntent =
-                intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT);
-
-        when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo);
-        when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null);
-
-        ContextWrapper fakeContext = new ContextWrapper(mContext) {
-            @Override
-            public PackageManager getPackageManager() {
-                return fakePackageMgr;
-            }
-        };
+        Context fakeContext = new FakeContextBuilder()
+                .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
+                .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
+                .build();
 
         TextClassifier fallback = TextClassifier.NO_OP;
         TextClassifier classifier = new TextClassifierImpl(
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 06ba15e..2ec35e9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -26,6 +26,8 @@
 import android.os.LocaleList;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -52,7 +54,11 @@
 
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object> textClassifierTypes() {
-        return Arrays.asList(LOCAL, SYSTEM);
+        return Arrays.asList(LOCAL);
+
+        // TODO: The following will fail on any device that specifies a no-op TextClassifierService.
+        // Enable when we can set a specified TextClassifierService for testing.
+        // return Arrays.asList(LOCAL, SYSTEM);
     }
 
     @Parameterized.Parameter
@@ -296,6 +302,17 @@
         assertTrue(links.getLinks().isEmpty());
     }
 
+    @Test
+    public void testApplyLinks_unsupportedCharacter() {
+        if (isTextClassifierDisabled()) return;
+        Spannable url = new SpannableString("\u202Emoc.diordna.com");
+        TextLinks.Request request = new TextLinks.Request.Builder(url).build();
+        assertEquals(
+                TextLinks.STATUS_UNSUPPORTED_CHARACTER,
+                mClassifier.generateLinks(request).apply(url, 0, null));
+    }
+
+
     @Test(expected = IllegalArgumentException.class)
     public void testGenerateLinks_tooLong() {
         if (isTextClassifierDisabled()) {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 70dc618..90758ba 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -983,6 +983,19 @@
     }
 
     @Test
+    public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
+        useSystemDefaultTextClassifier();
+        final String text = "\u202Emoc.diordna.com";
+        final TextView textView = mActivity.findViewById(R.id.textview);
+        mActivityRule.runOnUiThread(() -> textView.setText(text));
+        mInstrumentation.waitForIdleSync();
+
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+    }
+
+    @Test
     public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable {
         final List<SelectionEvent> selectionEvents = new ArrayList<>();
         final TextClassifier classifier = new TextClassifier() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 3cfc644..225515e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,6 +39,10 @@
         BatteryStatsUserLifecycleTests.class,
         KernelCpuProcReaderTest.class,
         KernelCpuProcStringReaderTest.class,
+        KernelCpuUidActiveTimeReaderTest.class,
+        KernelCpuUidClusterTimeReaderTest.class,
+        KernelCpuUidFreqTimeReaderTest.class,
+        KernelCpuUidUserSysTimeReaderTest.class,
         KernelMemoryBandwidthStatsTest.class,
         KernelSingleUidTimeReaderTest.class,
         KernelUidCpuFreqTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index dae9eb5..2663f2b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -37,6 +37,7 @@
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.CharBuffer;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -149,7 +150,7 @@
                             + "0 0 1 1 1 0 2 0 221",
                     iter.nextLine().toString());
             long[] actual = new long[43];
-            iter.nextLineAsArray(actual);
+            KernelCpuProcStringReader.asLongs(iter.nextLine(), actual);
             assertArrayEquals(
                     new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
@@ -183,7 +184,7 @@
         }
     }
 
-    /** Tests nextLineToArray functionality. */
+    /** Tests reading lines, then converting to long[]. */
     @Test
     public void testReadLineToArray() throws Exception {
         final long[][] data = getTestArray(800, 50);
@@ -193,12 +194,32 @@
         long[] actual = new long[50];
         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
             for (long[] expected : data) {
-                assertEquals(50, iter.nextLineAsArray(actual));
+                CharBuffer cb = iter.nextLine();
+                String before = cb.toString();
+                assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual));
                 assertArrayEquals(expected, actual);
+                assertEquals("Buffer not reset to the pos before reading", before, cb.toString());
             }
         }
     }
 
+    /** Tests error handling of converting to long[]. */
+    @Test
+    public void testLineToArrayErrorHandling() {
+        long[] actual = new long[100];
+        String invalidChar = "123: -1234 456";
+        String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123";
+        CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length());
+        assertEquals("Failed to report err for: " + invalidChar, -2,
+                KernelCpuProcStringReader.asLongs(cb, actual));
+        assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString());
+
+        cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length());
+        assertEquals("Failed to report err for: " + overflow, -3,
+                KernelCpuProcStringReader.asLongs(cb, actual));
+        assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString());
+    }
+
     /**
      * Tests that reading a file over the limit (1MB) will return null.
      */
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
new file mode 100644
index 0000000..adafda0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidActiveTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidActiveTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidActiveTimeReader mReader;
+    private VerifiableCallback mCallback;
+
+    private Random mRand = new Random(12345);
+    private final int mCpus = 4;
+    private final String mHeadline = "cpus: 4\n";
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidActiveTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[][] times = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], getActiveTime(times[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call will only return deltas.
+        mCallback.clear();
+        final long[][] newTimes1 = increaseTime(times);
+        writeToFile(mHeadline + uidLines(mUids, newTimes1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        mCallback.clear();
+        final long[][] newTimes2 = increaseTime(newTimes1);
+        writeToFile(mHeadline + uidLines(mUids, newTimes2));
+        mReader.readDelta(null);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        mCallback.clear();
+        final long[][] newTimes3 = increaseTime(newTimes2);
+        writeToFile(mHeadline + uidLines(mUids, newTimes3));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call should still return absolute values
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaDecreasedTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+        times2[0][0] = 100;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testReadDeltaNegativeTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time is -ve.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] *= -1;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j] / 10);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+            }
+        }
+        return newTime;
+    }
+
+    private long getActiveTime(long[] times) {
+        return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> {
+        SparseLongArray mData = new SparseLongArray();
+
+        public void verify(int uid, long time) {
+            assertEquals(time, mData.get(uid));
+            mData.delete(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, Long time) {
+            mData.put(uid, time);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
new file mode 100644
index 0000000..ad20d84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidClusterTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidClusterTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidClusterTimeReader mReader;
+    private VerifiableCallback mCallback;
+
+    private Random mRand = new Random(12345);
+    private final int mCpus = 6;
+    private final String mHeadline = "policy0: 4 policy4: 2\n";
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidClusterTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], clusterTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call will only return deltas.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(null);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        mCallback.clear();
+        final long[][] times4 = increaseTime(times3);
+        writeToFile(mHeadline + uidLines(mUids, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], clusterTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call should still return absolute values
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], clusterTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaDecreasedTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+        times2[0][0] = 100;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testReadDeltaNegativeTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] *= -1;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    private long[] clusterTime(long[] times) {
+        // Assumes 4 + 2 cores
+        return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
+                times[4] + times[5] / 2};
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j] / 10);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+            }
+        }
+        return newTime;
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+        SparseArray<long[]> mData = new SparseArray<>();
+
+        public void verify(int uid, long[] cpuTimes) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuTimes, array);
+            mData.remove(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, long[] times) {
+            long[] array = new long[times.length];
+            System.arraycopy(times, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
new file mode 100644
index 0000000..1d3a98a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidFreqTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidFreqTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidFreqTimeReader mReader;
+    private VerifiableCallback mCallback;
+    @Mock
+    private PowerProfile mPowerProfile;
+
+    private Random mRand = new Random(12345);
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
+        final long[][] freqs = {
+                {1, 12, 123, 1234},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
+                {1, 12, 123, 23, 2345, 234567}
+        };
+        final int[] numClusters = {2, 2, 3, 1};
+        final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
+        for (int i = 0; i < freqs.length; ++i) {
+            mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+            setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+            writeToFile(freqsLine(freqs[i]));
+            long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+                    Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+            assertFalse(errMsg, mReader.perClusterTimesAvailable());
+
+            // Verify that a second call won't read the proc file again
+            assertTrue(mTestFile.delete());
+            actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            assertFalse(errMsg, mReader.perClusterTimesAvailable());
+        }
+    }
+
+    @Test
+    public void testReadFreqs_perClusterTimesAvailable() throws Exception {
+        final long[][] freqs = {
+                {1, 12, 123, 1234},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
+        };
+        final int[] numClusters = {1, 2, 3};
+        final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
+        for (int i = 0; i < freqs.length; ++i) {
+            mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+            setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+            writeToFile(freqsLine(freqs[i]));
+            long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+                    Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+            assertTrue(errMsg, mReader.perClusterTimesAvailable());
+
+            // Verify that a second call won't read the proc file again
+            assertTrue(mTestFile.delete());
+            actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            assertTrue(errMsg, mReader.perClusterTimesAvailable());
+        }
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
+
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], times[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that readDelta also reads the frequencies if not already available.
+        assertTrue(mTestFile.delete());
+        long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+        assertArrayEquals(freqs, actualFreqs);
+
+        // Verify that a second call will only return deltas.
+        mCallback.clear();
+        final long[][] newTimes1 = increaseTime(times);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        mCallback.clear();
+        final long[][] newTimes2 = increaseTime(newTimes1);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+        mReader.readDelta(null);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        mCallback.clear();
+        final long[][] newTimes3 = increaseTime(newTimes2);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that readDelta also reads the frequencies if not already available.
+        assertTrue(mTestFile.delete());
+        long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+        assertArrayEquals(freqs, actualFreqs);
+
+        // Verify that a second call should still return absolute values
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times2[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaWrongData() throws Exception {
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] = 1000;
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times3[i], times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there is no callback if any value in the proc file is -ve.
+        mCallback.clear();
+        final long[][] times4 = increaseTime(times3);
+        times4[0][0] *= -1;
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the internal state was not modified when the proc file had -ve value.
+        mCallback.clear();
+        final long[][] times5 = increaseTime(times4);
+        times5[0] = increaseTime(times3)[0];
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
+        }
+
+        assertTrue(mTestFile.delete());
+    }
+
+    private String freqsLine(long[] freqs) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("uid:");
+        for (int i = 0; i < freqs.length; ++i) {
+            sb.append(" " + freqs[i]);
+        }
+        return sb.append('\n').toString();
+    }
+
+    private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
+        assertEquals(numClusters, clusterFreqs.length);
+        when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
+        for (int i = 0; i < numClusters; ++i) {
+            when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
+        }
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j] / 10);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 10 + 10;
+            }
+        }
+        return newTime;
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+        SparseArray<long[]> mData = new SparseArray<>();
+
+        public void verify(int uid, long[] cpuTimes) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuTimes, array);
+            mData.remove(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, long[] times) {
+            long[] array = new long[times.length];
+            System.arraycopy(times, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
new file mode 100644
index 0000000..9b4512b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidUserSysTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidUserSysTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidUserSysTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
+    private VerifiableCallback mCallback;
+
+    private Random mRand = new Random(12345);
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+    private final long[][] mInitialTimes = new long[][]{
+            {15334000, 310964000},
+            {537000, 114000},
+            {40000, 10000},
+            {170000, 57000},
+            {5377000, 867000},
+            {47000, 17000}
+    };
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidUserSysTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testThrottler() throws Exception {
+        mReader = new KernelCpuUidUserSysTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
+        mReader.setThrottle(500);
+
+        writeToFile(uidLines(mUids, mInitialTimes));
+        mReader.readDelta(mCallback);
+        assertEquals(6, mCallback.mData.size());
+
+        long[][] times1 = increaseTime(mInitialTimes);
+        writeToFile(uidLines(mUids, times1));
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        assertEquals(0, mCallback.mData.size());
+
+        SystemClock.sleep(600);
+
+        long[][] times2 = increaseTime(times1);
+        writeToFile(uidLines(mUids, times2));
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        assertEquals(6, mCallback.mData.size());
+
+        long[][] times3 = increaseTime(times2);
+        writeToFile(uidLines(mUids, times3));
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        assertEquals(0, mCallback.mData.size());
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[][] times1 = mInitialTimes;
+        writeToFile(uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that a second call will only return deltas.
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        final long[][] times3 = increaseTime(times2);
+        writeToFile(uidLines(mUids, times3));
+        mReader.readDelta(null);
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        final long[][] times4 = increaseTime(times3);
+        writeToFile(uidLines(mUids, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaWrongData() throws Exception {
+        final long[][] times1 = mInitialTimes;
+        writeToFile(uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] = 1000;
+        writeToFile(uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[][] times1 = mInitialTimes;
+        writeToFile(uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that a second call should still return absolute values
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times2[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+        assertTrue(mTestFile.delete());
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j]);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(1000) * 1000 + 1000;
+            }
+        }
+        return newTime;
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+        SparseArray<long[]> mData = new SparseArray<>();
+
+        public void verify(int uid, long[] cpuTimes) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuTimes, array);
+            mData.remove(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, long[] times) {
+            long[] array = new long[times.length];
+            System.arraycopy(times, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index a4c5ed2..141948f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -201,6 +201,24 @@
         <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     </split-permission>
 
+    <!-- Apps holding either the legacy READ or WRITE permissions will inherit
+         the ability to <em>read</em> new typed permissions in the Q release; they
+         won't gain the ability to <em>write</em> that content. -->
+    <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+    <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+                      targetSdk="10000">
+        <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+        <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+        <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+    </split-permission>
+    <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+    <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"
+                      targetSdk="10000">
+        <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+        <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+        <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+    </split-permission>
+
     <!-- This is a list of all the libraries available for application
          code to link against. -->
 
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 0119a02..dea2f45 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -670,7 +670,7 @@
 
     @Override
     public int getAlpha() {
-        return PixelFormat.TRANSLUCENT;
+        return mPaint.getAlpha();
     }
 
     @Override
@@ -714,10 +714,7 @@
 
     @Override
     public int getOpacity() {
-        if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
-            return mLayerState.mOpacityOverride;
-        }
-        return mLayerState.getOpacity();
+        return PixelFormat.TRANSLUCENT;
     }
 
     @Override
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index f0a3a95..1b5cb60 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -27,6 +27,7 @@
 namespace uirenderer {
 
 #define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define SK_MATRIX_STRING_V "[%.9f %.9f %.9f] [%.9f %.9f %.9f] [%.9f %.9f %.9f]"
 #define SK_MATRIX_ARGS(m)                                                                      \
     (m)->get(0), (m)->get(1), (m)->get(2), (m)->get(3), (m)->get(4), (m)->get(5), (m)->get(6), \
             (m)->get(7), (m)->get(8)
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 13d2dae..0cd6406 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <utils/MathUtils.h>
 #include "LayerDrawable.h"
 
 #include "GrBackendSurface.h"
@@ -32,6 +33,24 @@
     }
 }
 
+// This is a less-strict matrix.isTranslate() that will still report being translate-only
+// on imperceptibly small scaleX & scaleY values.
+static bool isBasicallyTranslate(const SkMatrix& matrix) {
+    if (!matrix.isScaleTranslate()) return false;
+    return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+}
+
+static bool shouldFilter(const SkMatrix& matrix) {
+    if (!matrix.isScaleTranslate()) return true;
+
+    // We only care about meaningful scale here
+    bool noScale = MathUtils::isOne(matrix.getScaleX())
+            && MathUtils::isOne(matrix.getScaleY());
+    bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
+            && SkScalarIsInt(matrix.getTranslateY());
+    return !(noScale && pixelAligned);
+}
+
 bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
                               const SkRect* srcRect, const SkRect* dstRect,
                               bool useLayerTransform) {
@@ -101,7 +120,7 @@
             // Integer translation is defined as when src rect and dst rect align fractionally.
             // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
             // only for SrcOver blending and without color filter (readback uses Src blending).
-            bool isIntegerTranslate = totalMatrix.isTranslate()
+            bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
                     && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
                     == SkScalarFraction(skiaSrcRect.fLeft)
                     && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
@@ -112,10 +131,7 @@
             canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
                                   SkCanvas::kFast_SrcRectConstraint);
         } else {
-            bool isIntegerTranslate = totalMatrix.isTranslate()
-                    && SkScalarIsInt(totalMatrix[SkMatrix::kMTransX])
-                    && SkScalarIsInt(totalMatrix[SkMatrix::kMTransY]);
-            if (layer->getForceFilter() || !isIntegerTranslate) {
+            if (layer->getForceFilter() || shouldFilter(totalMatrix)) {
                 paint.setFilterQuality(kLow_SkFilterQuality);
             }
             canvas->drawImage(layerImage.get(), 0, 0, &paint);
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 5475898..cc8d83f 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -34,6 +34,10 @@
         return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
     }
 
+    inline static bool isOne(float value) {
+        return areEqual(value, 1.0f);
+    }
+
     inline static bool isPositive(float value) { return value >= NON_ZERO_EPSILON; }
 
     /**
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 7627cf6..180183e 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -26,7 +26,9 @@
 oneway interface ILocationListener
 {
     void onLocationChanged(in Location location);
-    void onStatusChanged(String provider, int status, in Bundle extras);
     void onProviderEnabled(String provider);
     void onProviderDisabled(String provider);
+
+    // --- deprecated ---
+    void onStatusChanged(String provider, int status, in Bundle extras);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index b5d835a..ff2fad4 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -99,9 +99,10 @@
     void clearTestProviderLocation(String provider, String opPackageName);
     void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
     void clearTestProviderEnabled(String provider, String opPackageName);
+
+    // --- deprecated ---
     void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
             String opPackageName);
-    void clearTestProviderStatus(String provider, String opPackageName);
 
     boolean sendExtraCommand(String provider, String command, inout Bundle extras);
 
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 88904c8..aa9dddc 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -44,29 +44,12 @@
     void onLocationChanged(Location location);
 
     /**
-     * Called when the provider status changes. This method is called when
-     * a provider is unable to fetch a location or if the provider has recently
-     * become available after a period of unavailability.
+     * This callback will never be invoked and providers can be considers as always in the
+     * {@link LocationProvider#AVAILABLE} state.
      *
-     * @param provider the name of the location provider associated with this
-     * update.
-     * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
-     * provider is out of service, and this is not expected to change in the
-     * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
-     * the provider is temporarily unavailable but is expected to be available
-     * shortly; and {@link LocationProvider#AVAILABLE} if the
-     * provider is currently available.
-     * @param extras an optional Bundle which will contain provider specific
-     * status variables.
-     *
-     * <p> A number of common key/value pairs for the extras Bundle are listed
-     * below. Providers that use any of the keys on this list must
-     * provide the corresponding value as described below.
-     *
-     * <ul>
-     * <li> satellites - the number of satellites used to derive the fix
-     * </ul>
+     * @deprecated This callback will never be invoked.
      */
+    @Deprecated
     void onStatusChanged(String provider, int status, Bundle extras);
 
     /**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 02680ab..b66ceef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -146,9 +146,14 @@
     public static final String KEY_PROXIMITY_ENTERING = "entering";
 
     /**
+     * This key is no longer in use.
+     *
      * Key used for a Bundle extra holding an Integer status value
      * when a status change is broadcast using a PendingIntent.
+     *
+     * @deprecated Status changes are deprecated and no longer broadcast.
      */
+    @Deprecated
     public static final String KEY_STATUS_CHANGED = "status";
 
     /**
@@ -1581,8 +1586,7 @@
     }
 
     /**
-     * Sets mock status values for the given provider.  These values will be used in place
-     * of any actual values from the provider.
+     * This method has no effect as provider status has been deprecated and is no longer supported.
      *
      * @param provider the provider name
      * @param status the mock status
@@ -1593,7 +1597,10 @@
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
      * @throws IllegalArgumentException if no provider with the given name exists
+     *
+     * @deprecated This method has no effect.
      */
+    @Deprecated
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
         try {
             mService.setTestProviderStatus(provider, status, extras, updateTime,
@@ -1604,21 +1611,19 @@
     }
 
     /**
-     * Removes any mock status values associated with the given provider.
+     * This method has no effect as provider status has been deprecated and is no longer supported.
      *
      * @param provider the provider name
-     *
      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
      * @throws IllegalArgumentException if no provider with the given name exists
+     *
+     * @deprecated This method has no effect.
      */
+    @Deprecated
     public void clearTestProviderStatus(String provider) {
-        try {
-            mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L);
     }
 
     // --- GPS-specific support ---
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index c4fd097..b69a9d7 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -34,8 +34,23 @@
  * user-specified criteria.
  */
 public class LocationProvider {
+
+    /**
+     * @deprecated Location provider statuses are no longer supported.
+     */
+    @Deprecated
     public static final int OUT_OF_SERVICE = 0;
+
+    /**
+     * @deprecated Location provider statuses are no longer supported.
+     */
+    @Deprecated
     public static final int TEMPORARILY_UNAVAILABLE = 1;
+
+    /**
+     * @deprecated Location provider statuses are no longer supported.
+     */
+    @Deprecated
     public static final int AVAILABLE = 2;
 
     /**
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 1e69f16..d19559e 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,8 +11,8 @@
     method public abstract void onDisable();
     method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public abstract void onEnable();
-    method public abstract int onGetStatus(android.os.Bundle);
-    method public abstract long onGetStatusUpdateTime();
+    method public deprecated int onGetStatus(android.os.Bundle);
+    method public deprecated long onGetStatusUpdateTime();
     method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
     method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
     method public final void reportLocation(android.location.Location);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 30655f5..d45a4ba 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,14 +16,11 @@
 
 package com.android.location.provider;
 
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-
 import android.content.Context;
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
+import android.location.LocationProvider;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -36,6 +33,10 @@
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.FastPrintWriter;
 
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+
 /**
  * Base class for location providers implemented as unbundled services.
  *
@@ -173,6 +174,8 @@
     }
 
     /**
+     * This method will no longer be invoked.
+     *
      * Returns a information on the status of this provider.
      * <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
      * out of service, and this is not expected to change in the near
@@ -183,10 +186,17 @@
      *
      * <p>If extras is non-null, additional status information may be
      * added to it in the form of provider-specific key/value pairs.
+     *
+     * @deprecated This method will no longer be invoked.
      */
-    public abstract int onGetStatus(Bundle extras);
+    @Deprecated
+    public int onGetStatus(Bundle extras) {
+        return LocationProvider.AVAILABLE;
+    }
 
     /**
+     * This method will no longer be invoked.
+     *
      * Returns the time at which the status was last updated. It is the
      * responsibility of the provider to appropriately set this value using
      * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
@@ -195,8 +205,13 @@
      * the same status again.
      *
      * @return time of last status update in millis since last reboot
+     *
+     * @deprecated This method will no longer be invoked.
      */
-    public abstract long onGetStatusUpdateTime();
+    @Deprecated
+    public long onGetStatusUpdateTime() {
+        return 0;
+    }
 
     /**
      * Implements addditional location provider specific additional commands.
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 976d380..ff1bdd4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1911,7 +1911,7 @@
      *   system failed to generate a new session, a condition in which audio playback or recording
      *   will subsequently fail as well.
      */
-    public static int generateAudioSessionId() {
+    public int generateAudioSessionId() {
         int session = AudioSystem.newAudioSessionId();
         if (session > 0) {
             return session;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 995ebb2..dfe29e9 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1135,6 +1135,10 @@
                 maxChannels = 6;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
                 maxChannels = 16;
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
+                sampleRates = new int[] { 44100, 48000, 96000, 192000 };
+                bitRates = Range.create(16000, 2688000);
+                maxChannels = 24;
             } else {
                 Log.w(TAG, "Unsupported mime " + mime);
                 mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index c203fa9..7785900 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -434,9 +434,12 @@
      */
     @NonNull
     public List<AudioPresentation> getAudioPresentations(int trackIndex) {
-        return new ArrayList<AudioPresentation>();
+        return native_getAudioPresentations(trackIndex);
     }
 
+    @NonNull
+    private native List<AudioPresentation> native_getAudioPresentations(int trackIndex);
+
     /**
      * Get the PSSH info if present.
      * @return a map of uuid-to-bytes, with the uuid specifying
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5dee16e..284e422 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -138,6 +138,7 @@
     public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
     public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
     public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+    public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
     public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
 
     /**
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index 5306de6..a3adddd 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -14,173 +14,135 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
-#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_
 
 #include "jni.h"
 
-#include <media/AudioPresentationInfo.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-
+#include <media/stagefright/foundation/ADebug.h>  // CHECK
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 namespace android {
 
 struct JAudioPresentationInfo {
     struct fields_t {
-        jclass      clazz;
+        jclass      clazz = NULL;
         jmethodID   constructID;
 
         // list parameters
-        jclass listclazz;
+        jclass listClazz = NULL;
         jmethodID listConstructId;
         jmethodID listAddId;
 
+        // hashmap parameters
+        jclass hashMapClazz = NULL;
+        jmethodID hashMapConstructID;
+        jmethodID hashMapPutID;
+
+        // ulocale parameters
+        jclass ulocaleClazz = NULL;
+        jmethodID ulocaleConstructID;
+
         void init(JNIEnv *env) {
             jclass lclazz = env->FindClass("android/media/AudioPresentation");
-            if (lclazz == NULL) {
-                return;
-            }
-
+            CHECK(lclazz != NULL);
             clazz = (jclass)env->NewGlobalRef(lclazz);
-            if (clazz == NULL) {
-                return;
-            }
-
+            CHECK(clazz != NULL);
             constructID = env->GetMethodID(clazz, "<init>",
-                                "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
-            env->DeleteLocalRef(lclazz);
+                    "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
+            CHECK(constructID != NULL);
 
             // list objects
-            jclass llistclazz = env->FindClass("java/util/ArrayList");
-            CHECK(llistclazz != NULL);
-            listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
-            CHECK(listclazz != NULL);
-            listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+            jclass llistClazz = env->FindClass("java/util/ArrayList");
+            CHECK(llistClazz != NULL);
+            listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
+            CHECK(listClazz != NULL);
+            listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
             CHECK(listConstructId != NULL);
-            listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+            listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
             CHECK(listAddId != NULL);
-            env->DeleteLocalRef(llistclazz);
+
+            // hashmap objects
+            jclass lhashMapClazz = env->FindClass("java/util/HashMap");
+            CHECK(lhashMapClazz != NULL);
+            hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
+            CHECK(hashMapClazz != NULL);
+            hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
+            CHECK(hashMapConstructID != NULL);
+            hashMapPutID = env->GetMethodID(
+                    hashMapClazz,
+                    "put",
+                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+            CHECK(hashMapPutID != NULL);
+
+            jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
+            CHECK(lulocaleClazz != NULL);
+            ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
+            CHECK(ulocaleClazz != NULL);
+            ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
+            CHECK(ulocaleConstructID != NULL);
         }
 
         void exit(JNIEnv *env) {
-            env->DeleteGlobalRef(clazz);
-            clazz = NULL;
-            env->DeleteGlobalRef(listclazz);
-            listclazz = NULL;
+            env->DeleteGlobalRef(clazz); clazz = NULL;
+            env->DeleteGlobalRef(listClazz); listClazz = NULL;
+            env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
+            env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
         }
     };
 
-    static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
-        ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
-
-        if (hashMapClazz.get() == NULL) {
-            return -EINVAL;
-        }
-        jmethodID hashMapConstructID =
-            env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
-        if (hashMapConstructID == NULL) {
-            return -EINVAL;
-        }
-        jmethodID hashMapPutID =
-            env->GetMethodID(
-                    hashMapClazz.get(),
-                    "put",
-                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
-        if (hashMapPutID == NULL) {
-            return -EINVAL;
-        }
-
-        jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
-        for (size_t i = 0; i < msg->countEntries(); ++i) {
-            AMessage::Type valueType;
-            const char *key = msg->getEntryNameAt(i, &valueType);
-
-            if (!strncmp(key, "android._", 9)) {
-                // don't expose private keys (starting with android._)
-                continue;
-            }
-            jobject valueObj = NULL;
-            AString val;
-            CHECK(msg->findString(key, &val));
-            valueObj = env->NewStringUTF(val.c_str());
-            if (valueObj != NULL) {
-                ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
-                if (localeClazz.get() == NULL) {
-                    return -EINVAL;
-                }
-                jmethodID localeConstructID =
-                        env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
-                if (localeConstructID == NULL) {
-                    return -EINVAL;
-                }
-                jstring jLanguage = env->NewStringUTF(key);
-                jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
-                env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
-                env->DeleteLocalRef(jLocale); jLocale = NULL;
-                env->DeleteLocalRef(valueObj); valueObj = NULL;
-                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
-            }
-        }
-
-        *map = hashMap;
-
-        return OK;
+    static jobject asJobject(JNIEnv *env, const fields_t& fields) {
+        return env->NewObject(fields.listClazz, fields.listConstructId);
     }
 
-    jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
-        jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
-
-        for (size_t i = 0; i < info.countPresentations(); ++i) {
-            const sp<AudioPresentation> &ap = info.getPresentation(i);
-            jobject jLabelObject;
-
-            sp<AMessage> labelMessage = new AMessage();
-            for (size_t i = 0; i < ap->mLabels.size(); ++i) {
-                labelMessage->setString(ap->mLabels.keyAt(i).string(),
-                                        ap->mLabels.valueAt(i).string());
+    static void addPresentations(JNIEnv *env, const fields_t& fields,
+                    const AudioPresentationCollection& presentations, jobject presentationsJObj) {
+        for (const auto& ap : presentations) {
+            ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
+            if (jLabelObject == nullptr) return;
+            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
+            if (jLanguage == nullptr) return;
+            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+                            fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
+            ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
+                            static_cast<jint>(ap.mPresentationId),
+                            static_cast<jint>(ap.mProgramId),
+                            jLocale.get(),
+                            static_cast<jint>(ap.mMasteringIndication),
+                            static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
+                            static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
+                            static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
+                            jLabelObject.get()));
+            if (jValueObj != nullptr) {
+                env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
             }
-            if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
-                return NULL;
-            }
-            ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
-            if (localeClazz.get() == NULL) {
-                return NULL;
-            }
-            jmethodID localeConstructID =
-                    env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
-            if (localeConstructID == NULL) {
-                return NULL;
-            }
-            jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
-            jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
-            jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
-                                static_cast<jint>(ap->mPresentationId),
-                                static_cast<jint>(ap->mProgramId),
-                                jLocale,
-                                static_cast<jint>(ap->mMasteringIndication),
-                                static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
-                                    1 : 0),
-                                static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
-                                    1 : 0),
-                                static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
-                                    1 : 0),
-                                jLabelObject);
-            if (jValueObj == NULL) {
-                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
-                return NULL;
-            }
-
-            env->CallBooleanMethod(list, fields.listAddId, jValueObj);
-            env->DeleteLocalRef(jLocale); jLocale = NULL;
-            env->DeleteLocalRef(jValueObj); jValueObj = NULL;
-            env->DeleteLocalRef(jLanguage); jLanguage = NULL;
         }
-        return list;
+    }
+
+  private:
+    static ScopedLocalRef<jobject> convertLabelsToMap(
+            JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
+        ScopedLocalRef<jobject> nullMap(env, nullptr);
+        ScopedLocalRef<jobject> hashMap(env, env->NewObject(
+                        fields.hashMapClazz, fields.hashMapConstructID));
+        if (hashMap == nullptr) {
+            return nullMap;
+        }
+
+        for (const auto& label : labels) {
+            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
+            if (jLanguage == nullptr) return nullMap;
+            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+                            fields.ulocaleClazz,
+                            fields.ulocaleConstructID,
+                            jLanguage.get()));
+            if (jLocale == nullptr) return nullMap;
+            ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
+            if (jValue == nullptr) return nullMap;
+            env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
+        }
+        return hashMap;
     }
 };
 }  // namespace android
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 15957c6..29238d3 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "MediaExtractor-JNI"
 #include <utils/Log.h>
 
+#include "android_media_AudioPresentation.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_MediaExtractor.h"
 #include "android_media_MediaMetricsJNI.h"
@@ -56,6 +57,7 @@
 };
 
 static fields_t gFields;
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
 
 JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
     : mClass(NULL),
@@ -289,6 +291,10 @@
     return mImpl->getCachedDuration(durationUs, eos);
 }
 
+status_t JMediaExtractor::getAudioPresentations(size_t trackIdx,
+        AudioPresentationCollection *presentations) const {
+    return mImpl->getAudioPresentations(trackIdx, presentations);
+}
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -668,6 +674,28 @@
     return JNI_TRUE;
 }
 
+static jobject android_media_MediaExtractor_getAudioPresentations(
+        JNIEnv *env, jobject thiz, jint trackIdx) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+    jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return presentationsJObj;
+    }
+    AudioPresentationCollection presentations;
+    status_t err = extractor->getAudioPresentations(trackIdx, &presentations);
+    if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) {
+        return presentationsJObj;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return presentationsJObj;
+    }
+
+    JAudioPresentationInfo::addPresentations(
+            env, gAudioPresentationFields, presentations, presentationsJObj);
+    return presentationsJObj;
+}
+
 static void android_media_MediaExtractor_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/MediaExtractor");
     CHECK(clazz != NULL);
@@ -683,6 +711,8 @@
 
     gFields.cryptoInfoSetPatternID =
         env->GetMethodID(clazz, "setPattern", "(II)V");
+
+    gAudioPresentationFields.init(env);
 }
 
 static void android_media_MediaExtractor_native_setup(
@@ -963,6 +993,9 @@
 
     {"native_getMetrics",          "()Landroid/os/PersistableBundle;",
       (void *)android_media_MediaExtractor_native_getMetrics},
+
+    { "native_getAudioPresentations", "(I)Ljava/util/List;",
+      (void *)android_media_MediaExtractor_getAudioPresentations },
 };
 
 int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index aaa8421..baa779c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -18,6 +18,7 @@
 #define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <media/MediaSource.h>
 #include <media/DataSource.h>
 #include <utils/Errors.h>
@@ -66,6 +67,8 @@
     status_t getMetrics(Parcel *reply) const;
 
     bool getCachedDuration(int64_t *durationUs, bool *eos) const;
+    status_t getAudioPresentations(size_t trackIdx,
+            AudioPresentationCollection *presentations) const;
 
 protected:
     virtual ~JMediaExtractor();
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index 7cb49da..e3e52b1 100644
--- a/native/webview/plat_support/draw_gl_functor.cpp
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -42,10 +42,10 @@
 class DrawGLFunctor : public Functor {
  public:
   explicit DrawGLFunctor(jlong view_context) : view_context_(view_context) {}
-  virtual ~DrawGLFunctor() {}
+  ~DrawGLFunctor() override {}
 
   // Functor
-  virtual status_t operator ()(int what, void* data) {
+  status_t operator ()(int what, void* data) override {
     using uirenderer::DrawGlInfo;
     if (!g_aw_drawgl_function) {
       ALOGE("Cannot draw: no DrawGL Function installed");
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 8f13497..f244f9f 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -45,6 +45,7 @@
         "androidx.slice_slice-builders",
         "androidx.arch.core_core-runtime",
         "androidx.lifecycle_lifecycle-extensions",
+        "car-theme-lib-bp",
         "SystemUI-tags",
         "SystemUI-proto",
     ],
diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
index c98740e..709797d 100644
--- a/packages/CarSystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -20,11 +20,9 @@
     android:id="@+id/volume_list"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@android:color/black"
     android:minWidth="@dimen/volume_dialog_panel_width"
-    android:theme="@style/Theme.Car.DialogListView"
-    app:dividerEndMargin="@dimen/car_keyline_1"
-    app:dividerStartMargin="@dimen/car_keyline_1"
+    android:theme="@style/PagedListViewTheme"
     app:gutter="none"
     app:scrollBarEnabled="false"
+    app:listDividerColor="@color/list_divider_color"
     app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index df8f8db..c510ab6 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -51,4 +51,6 @@
     <color name="car_grey_900">#ff212121</color>
 
     <color name="keyguard_button_text_color">@android:color/black</color>
+
+    <color name="list_divider_color">@*android:color/car_list_divider_light</color>
 </resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7f4544a..0d95d30 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -41,15 +41,16 @@
         <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
     </style>
 
-    <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
-        <item name="android:colorControlActivated">@color/car_accent</item>
-        <item name="listItemBackgroundColor">@android:color/black</item>
-    </style>
-
     <style name="NavigationBarButton">
         <item name="android:layout_height">96dp</item>
         <item name="android:layout_width">96dp</item>
         <item name="android:background">@drawable/nav_button_background</item>
     </style>
 
-</resources>
+    <style name="PagedListViewTheme" parent="@style/Theme.CarSupportWrapper.NoActionBar">
+        <item name="android:background">@*android:color/car_background</item>
+        <item name="listItemBackgroundColor">@*android:color/car_background</item>
+        <item name="dividerEndMargin">@dimen/car_keyline_1</item>
+        <item name="dividerStartMargin">@dimen/car_keyline_1</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 60153fcb..22d0e3b 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -355,6 +355,29 @@
     }
 
     @Override
+    public void onNotificationExpansionChanged(String key, boolean isUserAction,
+            boolean isExpanded) {
+        if (DEBUG) {
+            Log.i(TAG,
+                    "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
+                            + ", isExpanded = isExpanded");
+        }
+    }
+
+    @Override
+    public void onNotificationDirectReply(String key) {
+        if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+    }
+
+    @Override
+    public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+        if (DEBUG) {
+            Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
+                    + "], source = [" + source + "]");
+        }
+    }
+
+    @Override
     public void onListenerConnected() {
         if (DEBUG) Log.i(TAG, "CONNECTED");
         try {
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 56feb47..87d6e4a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -16,27 +16,24 @@
 
 package com.android.location.fused;
 
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.ProviderPropertiesUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.location.Criteria;
-import android.location.LocationProvider;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.UserHandle;
 import android.os.WorkSource;
 
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.ProviderPropertiesUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
     private static final String TAG = "FusedLocationProvider";
 
@@ -48,7 +45,6 @@
     private static final int MSG_DISABLE = 2;
     private static final int MSG_SET_REQUEST = 3;
 
-    private final Context mContext;
     private final FusionEngine mEngine;
 
     private static class RequestWrapper {
@@ -62,13 +58,12 @@
 
     public FusedLocationProvider(Context context) {
         super(TAG, PROPERTIES);
-        mContext = context;
         mEngine = new FusionEngine(context, Looper.myLooper());
 
         // listen for user change
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+        context.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
@@ -122,14 +117,4 @@
         // perform synchronously
         mEngine.dump(fd, pw, args);
     }
-
-    @Override
-    public int onGetStatus(Bundle extras) {
-        return LocationProvider.AVAILABLE;
-    }
-
-    @Override
-    public long onGetStatusUpdateTime() {
-        return 0;
-    }
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d1f140f..444e724 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,6 +16,7 @@
         "SettingsLibAppPreference",
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
+        "SettingsLayoutPreference",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
new file mode 100644
index 0000000..489d360
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+    name: "SettingsLayoutPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+          "androidx.preference_preference",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
new file mode 100644
index 0000000..ee4ce49
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
new file mode 100644
index 0000000..0678263
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/entity_header"
+    style="@style/EntityHeader"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:id="@+id/entity_header_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/entity_header_icon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="fitXY"
+            android:antialias="true"/>
+
+        <TextView
+            android:id="@+id/entity_header_title"
+            style="@style/TextAppearance.EntityHeaderTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="false"
+            android:ellipsize="marquee"
+            android:textDirection="locale"
+            android:layout_marginTop="8dp"/>
+
+        <TextView
+            android:id="@+id/install_type"
+            style="@style/TextAppearance.EntityHeaderSummary"
+            android:visibility="gone"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"/>
+
+        <TextView
+            android:id="@+id/entity_header_summary"
+            style="@style/TextAppearance.EntityHeaderSummary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"/>
+
+        <TextView
+            android:id="@+id/entity_header_second_summary"
+            style="@style/TextAppearance.EntityHeaderSummary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/entity_header_links"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerVertical="true"
+        android:layout_alignParentEnd="true"
+        android:orientation="vertical">
+
+        <ImageButton
+            android:id="@android:id/button1"
+            style="?android:attr/actionOverflowButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_weight="1"
+            android:layout_height="0dp"
+            android:minWidth="24dp"
+            android:src="@null"
+            android:tint="?android:attr/colorAccent"/>
+
+        <ImageButton
+            android:id="@android:id/button2"
+            style="?android:attr/actionOverflowButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_weight="1"
+            android:layout_height="0dp"
+            android:minWidth="24dp"
+            android:src="@null"
+            android:tint="?android:attr/colorAccent"/>
+
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
new file mode 100644
index 0000000..805744b
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+
+<resources>
+    <style name="EntityHeader">
+        <item name="android:background">?android:attr/colorPrimaryDark</item>
+        <item name="android:paddingTop">24dp</item>
+        <item name="android:paddingBottom">16dp</item>
+        <item name="android:paddingEnd">16dp</item>
+    </style>
+
+    <style name="TextAppearance.EntityHeaderTitle"
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">20sp</item>
+    </style>
+
+    <style name="TextAppearance.EntityHeaderSummary"
+           parent="@android:style/TextAppearance.Material.Body1">
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:gravity">start</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
new file mode 100644
index 0000000..2a635b0
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 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.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference can be simply customized a view by adding layout attribute in xml.
+ * User also can decide whether or not LayoutPreference allows above divider or below divider.
+ *
+ * For instances,
+ *
+ * <com.android.settingslib.widget.LayoutPreference
+ *      ...
+ *      android:layout="@layout/settings_entity_header"
+ *      xxxxxxx:allowDividerAbove="true"
+ *      xxxxxxx:allowDividerBelow="true"
+ *
+ */
+public class LayoutPreference extends Preference {
+
+    private final View.OnClickListener mClickListener = v -> performClick(v);
+    private boolean mAllowDividerAbove;
+    private boolean mAllowDividerBelow;
+    private View mRootView;
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and the supplied
+     * attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     */
+    public LayoutPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs, 0 /* defStyleAttr */);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme, the supplied
+     * attribute set, and default style attribute.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     */
+    public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and a customized view id.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param resource The view id which you expected to be inflated and show in preference.
+     */
+    public LayoutPreference(Context context, int resource) {
+        this(context, LayoutInflater.from(context).inflate(resource, null, false));
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and a customized view.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param view The view which you expected show in preference.
+     */
+    public LayoutPreference(Context context, View view) {
+        super(context);
+        setView(view);
+    }
+
+    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
+        mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
+                R.styleable.Preference_allowDividerAbove, false);
+        mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
+                R.styleable.Preference_allowDividerBelow, false);
+        a.recycle();
+
+        a = context.obtainStyledAttributes(
+                attrs, R.styleable.Preference, defStyleAttr, 0);
+        int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0);
+        if (layoutResource == 0) {
+            throw new IllegalArgumentException("LayoutPreference requires a layout to be defined");
+        }
+        a.recycle();
+
+        // Need to create view now so that findViewById can be called immediately.
+        final View view = LayoutInflater.from(getContext())
+                .inflate(layoutResource, null, false);
+        setView(view);
+    }
+
+    private void setView(View view) {
+        setLayoutResource(R.layout.layout_preference_frame);
+        mRootView = view;
+        setShouldDisableView(false);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        holder.itemView.setOnClickListener(mClickListener);
+
+        final boolean selectable = isSelectable();
+        holder.itemView.setFocusable(selectable);
+        holder.itemView.setClickable(selectable);
+        holder.setDividerAllowedAbove(mAllowDividerAbove);
+        holder.setDividerAllowedBelow(mAllowDividerBelow);
+
+        FrameLayout layout = (FrameLayout) holder.itemView;
+        layout.removeAllViews();
+        ViewGroup parent = (ViewGroup) mRootView.getParent();
+        if (parent != null) {
+            parent.removeView(mRootView);
+        }
+        layout.addView(mRootView);
+    }
+
+    /**
+     * Finds the view with the given ID.
+     *
+     * @param id the ID to search for
+     * @return a view with given ID if found, or {@code null} otherwise
+     */
+    public <T extends View> T findViewById(int id) {
+        return mRootView.findViewById(id);
+    }
+
+    /**
+     * LayoutPreference whether or not allows to set a below divider.
+     */
+    public void setAllowDividerBelow(boolean allowed) {
+        mAllowDividerBelow = allowed;
+    }
+
+    /**
+     * Return a value whether or not LayoutPreference allows to set a below divider.
+     */
+    public boolean isAllowDividerBelow() {
+        return mAllowDividerBelow;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 86928fc..4f81daf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -154,8 +154,10 @@
     static final String KEY_CARRIER_NAME = "key_carrier_name";
     static final AtomicInteger sLastId = new AtomicInteger(0);
 
-    /**
-     * These values are matched in string arrays -- changes must be kept in sync
+    /*
+     * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState,
+     * and sent across IPC. The numeric values should remain stable, otherwise the changes will need
+     * to be synced with other unbundled users of this library.
      */
     public static final int SECURITY_NONE = 0;
     public static final int SECURITY_WEP = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index ede248b..8757eed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -55,6 +55,8 @@
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
+                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/"
+                            + "SettingsLayoutPreference/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
                     paths.add(resourcePath("file:frameworks/base/core/res/res"));
                     paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
new file mode 100644
index 0000000..427a611
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class LayoutPreferenceTest {
+
+    private LayoutPreference mPreference;
+    private PreferenceViewHolder mHolder;
+
+    @Before
+    public void setUp() {
+        final Context mContext = RuntimeEnvironment.application;
+        mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+        mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
+                .inflate(R.layout.layout_preference_frame, null, false));
+    }
+
+    @Test
+    public void setOnPreferenceClickListener_layoutPreferenceShouldListenClickEvent() {
+        final OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
+
+        mPreference.setOnPreferenceClickListener(listener);
+        mPreference.onBindViewHolder(mHolder);
+
+        mHolder.itemView.callOnClick();
+
+        verify(listener).onPreferenceClick(mPreference);
+        assertThat(mHolder.itemView.isFocusable()).isTrue();
+        assertThat(mHolder.itemView.isClickable()).isTrue();
+    }
+
+    @Test
+    public void setNonSelectable_viewShouldNotBeSelectable() {
+        mPreference.setSelectable(false);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mHolder.itemView.isFocusable()).isFalse();
+        assertThat(mHolder.itemView.isClickable()).isFalse();
+    }
+
+    @Test
+    public void disableSomeView_shouldMaintainStateAfterBind() {
+        mPreference.findViewById(android.R.id.button1).setEnabled(false);
+        mPreference.findViewById(android.R.id.button2).setEnabled(true);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mPreference.findViewById(android.R.id.button1).isEnabled()).isFalse();
+        assertThat(mPreference.findViewById(android.R.id.button2).isEnabled()).isTrue();
+    }
+
+    @Test
+    public void allowDividerBelow_shouldSaveCorrectDividerStatus() {
+        mPreference.setAllowDividerBelow(true);
+
+        assertThat(mPreference.isAllowDividerBelow()).isTrue();
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index ba4eb5f..88b8dd8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -52,7 +52,9 @@
     }
 
     class Sensor {
-        public static int TYPE_WAKE_LOCK_SCREEN = 1;
+        public static final int TYPE_WAKE_LOCK_SCREEN = 1;
+        public static final int TYPE_WAKE_DISPLAY = 2;
+        public static final int TYPE_SWIPE = 3;
 
         int mType;
 
@@ -68,6 +70,7 @@
     class TriggerEvent {
         Sensor mSensor;
         int mVendorType;
+        float[] mValues;
 
         /**
          * Creates a trigger event
@@ -76,14 +79,30 @@
          *                   e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
          */
         public TriggerEvent(Sensor sensor, int vendorType) {
+            this(sensor, vendorType, null);
+        }
+
+        /**
+         * Creates a trigger event
+         * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
+         * @param vendorType The vendor type, which should be unique for each type of sensor,
+         *                   e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
+         * @param values Values captured by the sensor.
+         */
+        public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
             mSensor = sensor;
             mVendorType = vendorType;
+            mValues = values;
         }
 
         public Sensor getSensor() {
             return mSensor;
         }
 
+        public float[] getValues() {
+            return mValues;
+        }
+
         public int getVendorType() {
             return mVendorType;
         }
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index c86ebe7..eb3f70a 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -51,7 +51,7 @@
             android:layout_centerVertical="true"
             android:layout_toEndOf="@id/pkgicon" />
         <TextView
-            android:id="@+id/pkg_group_divider"
+            android:id="@+id/pkg_divider"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -61,7 +61,7 @@
             android:layout_centerVertical="true"
             android:layout_toEndOf="@id/pkgname" />
         <TextView
-            android:id="@+id/group_name"
+            android:id="@+id/delegate_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -70,7 +70,7 @@
             android:ellipsize="end"
             android:maxLines="1"
             android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/pkg_group_divider" />
+            android:layout_toEndOf="@id/pkg_divider" />
         <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
         <ImageButton
             android:id="@+id/info"
@@ -101,13 +101,39 @@
             android:layout_marginStart="@*android:dimen/notification_content_margin_start"
             android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
             android:orientation="vertical">
-            <!-- Channel Name -->
-            <TextView
-                android:id="@+id/channel_name"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                style="@android:style/TextAppearance.Material.Notification.Title" />
+            <RelativeLayout
+                android:id="@+id/names"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TextView
+                    android:id="@+id/group_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+                    android:layout_marginStart="2dp"
+                    android:layout_marginEnd="2dp"
+                    android:ellipsize="end"
+                    android:maxLines="1"
+                    android:layout_centerVertical="true" />
+                <TextView
+                    android:id="@+id/pkg_group_divider"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+                    android:layout_marginStart="2dp"
+                    android:layout_marginEnd="2dp"
+                    android:text="@*android:string/notification_header_divider_symbol"
+                    android:layout_centerVertical="true"
+                    android:layout_toEndOf="@id/group_name" />
+                <!-- Channel Name -->
+                <TextView
+                    android:id="@+id/channel_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    style="@android:style/TextAppearance.Material.Notification.Title"
+                    android:layout_toEndOf="@id/pkg_group_divider"/>
+            </RelativeLayout>
             <!-- Question prompt -->
             <TextView
                 android:id="@+id/block_prompt"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a9d0f68..c5654f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1574,6 +1574,9 @@
     <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
     <string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
 
+    <!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
+    <string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
     <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
     <string name="appops_camera">This app is using the camera.</string>
     <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
@@ -2251,7 +2254,10 @@
     <string name="heap_dump_tile_name">Dump SysUI Heap</string>
 
     <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
-    <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+    <plurals name="ongoing_privacy_chip_multiple_apps">
+        <item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+    </plurals>
 
     <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
     <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
@@ -2260,12 +2266,15 @@
     <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
 
     <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
-    <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</string>
+    <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+        <item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+    </plurals>
 
     <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
     <string name="ongoing_privacy_dialog_cancel">Cancel</string>
 
-    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
     <string name="ongoing_privacy_dialog_open_settings">View details</string>
 
     <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6b0a7a9..b55aa5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1531,10 +1531,11 @@
         }
         mHandler.removeCallbacks(mRetryFingerprintAuthentication);
         boolean shouldListenForFingerprint = shouldListenForFingerprint();
-        if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
+        boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+                || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+        if (runningOrRestarting && !shouldListenForFingerprint) {
             stopListeningForFingerprint();
-        } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
-                && shouldListenForFingerprint) {
+        } else if (!runningOrRestarting && shouldListenForFingerprint) {
             startListeningForFingerprint();
         }
     }
@@ -1589,6 +1590,10 @@
             setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
         }
+        if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            // Waiting for restart via handleFingerprintError().
+            return;
+        }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
         int userId = ActivityManager.getCurrentUser();
         if (isUnlockWithFingerprintPossible(userId)) {
@@ -2418,6 +2423,8 @@
                     + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
             pw.println("    disabled(DPM)=" + isFingerprintDisabled(userId));
             pw.println("    possible=" + isUnlockWithFingerprintPossible(userId));
+            pw.println("    listening: actual=" + mFingerprintRunningState
+                    + " expected=" + (shouldListenForFingerprint() ? 1 : 0));
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e868f96..c6dcfc7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -268,7 +268,7 @@
         for (BubbleView bv : mBubbles.values()) {
             NotificationData.Entry entry = bv.getEntry();
             if (entry != null) {
-                if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+                if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
                     viewsToRemove.add(bv);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index a79e047..6c47aac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -120,7 +120,7 @@
      * @return the view to display when the bubble is expanded.
      */
     public ExpandableNotificationRow getRowView() {
-        return mEntry.row;
+        return mEntry.getRow();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 21b21d9..eda3c59 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -177,9 +177,22 @@
         log("state " + state);
     }
 
-    public static void traceWakeLockScreenWakeUp() {
+    /**
+     * Appends lock screen wake up event to the logs.
+     * @param wake if we're waking up or sleeping.
+     */
+    public static void traceLockScreenWakeUp(boolean wake) {
         if (!ENABLED) return;
-        log("wakeLockScreenWakeUp");
+        log("wakeLockScreenWakeUp " + wake);
+    }
+
+    /**
+     * Appends wake-display event to the logs.
+     * @param wake if we're waking up or sleeping.
+     */
+    public static void traceWakeDisplay(boolean wake) {
+        if (!ENABLED) return;
+        log("wakeLockScreenWakeUp " + wake);
     }
 
     public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 77f7ad4f..bf8e04d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.doze;
 
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
 
 import android.annotation.AnyThread;
@@ -67,7 +68,6 @@
     private final AmbientDisplayConfiguration mConfig;
     private final WakeLock mWakeLock;
     private final Consumer<Boolean> mProxCallback;
-    private final Consumer<Boolean> mWakeScreenCallback;
     private final Callback mCallback;
 
     private final Handler mHandler = new Handler();
@@ -76,8 +76,7 @@
 
     public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
-            Callback callback, Consumer<Boolean> proxCallback,
-            Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
+            Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
         mContext = context;
         mAlarmManager = alarmManager;
         mSensorManager = sensorManager;
@@ -85,7 +84,6 @@
         mConfig = config;
         mWakeLock = wakeLock;
         mProxCallback = proxCallback;
-        mWakeScreenCallback = wakeScreenCallback;
         mResolver = mContext.getContentResolver();
 
         mSensors = new TriggerSensor[] {
@@ -123,7 +121,13 @@
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
-                new WakeScreenSensor(),
+                new PluginTriggerSensor(
+                        new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
+                        Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
+                        true /* configured */,
+                        DozeLog.REASON_SENSOR_WAKE_UP,
+                        false /* reports touch coordinates */,
+                        false /* touchscreen */),
         };
 
         mProxSensor = new ProxSensor(policy);
@@ -395,7 +399,8 @@
                     screenX = event.values[0];
                     screenY = event.values[1];
                 }
-                mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY);
+                mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
+                        event.values);
                 updateListener();  // reregister, this sensor only fires once
             }));
         }
@@ -429,7 +434,14 @@
 
         private final SensorManagerPlugin.Sensor mPluginSensor;
         private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
-            onTrigger(null);
+            DozeLog.traceSensor(mContext, mPulseReason);
+            mHandler.post(mWakeLock.wrap(() -> {
+                if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+                mRegistered = false;
+                mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+                        event.getValues());
+                updateListener();  // reregister, this sensor only fires once
+            }));
         };
 
         PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
@@ -463,28 +475,19 @@
                     .append(", mSensor=").append(mPluginSensor).append("}").toString();
         }
 
-    }
-
-    private class WakeScreenSensor extends TriggerSensor {
-
-        WakeScreenSensor() {
-            super(findSensorWithType(mConfig.wakeScreenSensorType()),
-                    Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
-                    DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
-                    false /* requiresTouchscreen */);
+        private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+            if (event == null) return null;
+            final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
+                    .append(event.getSensor()).append(',')
+                    .append(event.getVendorType());
+            if (event.getValues() != null) {
+                for (int i = 0; i < event.getValues().length; i++) {
+                    sb.append(',').append(event.getValues()[i]);
+                }
+            }
+            return sb.append(']').toString();
         }
 
-        @Override
-        @AnyThread
-        public void onTrigger(TriggerEvent event) {
-            DozeLog.traceSensor(mContext, mPulseReason);
-            mHandler.post(mWakeLock.wrap(() -> {
-                if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
-                mRegistered = false;
-                mWakeScreenCallback.accept(event.values[0] > 0);
-                updateListener();  // reregister, this sensor only fires once
-            }));
-        }
     }
 
     public interface Callback {
@@ -494,11 +497,11 @@
          * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
          * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
          * @param screenX the location on the screen where the sensor fired or -1
-         *                if the sensor doesn't support reporting screen locations.
+ *                if the sensor doesn't support reporting screen locations.
          * @param screenY the location on the screen where the sensor fired or -1
-         *                if the sensor doesn't support reporting screen locations.
+         * @param rawValues raw values array from the event.
          */
         void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck,
-                float screenX, float screenY);
+                float screenX, float screenY, float[] rawValues);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index d69b1bf..bad0148 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
-                config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
+                config, wakeLock, this::onSensor, this::onProximityFar,
                 dozeParameters.getPolicy());
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
     }
@@ -124,13 +124,17 @@
     }
 
     private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
-            float screenX, float screenY) {
+            float screenX, float screenY, float[] rawValues) {
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
         boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+        boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+        boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
 
-        if (isLongPress) {
+        if (isWakeDisplay) {
+            onWakeScreen(wakeEvent);
+        } else if (isLongPress) {
             requestPulse(pulseReason, sensorPerformedProxCheck);
         } else {
             proximityCheckThenCall((result) -> {
@@ -141,7 +145,15 @@
                 if (isDoubleTap) {
                     mDozeHost.onDoubleTap(screenX, screenY);
                     mMachine.wakeUp();
-                } else if (isPickup || isWakeLockScreen) {
+                } else if (isWakeLockScreen) {
+                    if (wakeEvent) {
+                        mDozeHost.setPassiveInterrupt(true);
+                        mMachine.wakeUp();
+                        DozeLog.traceLockScreenWakeUp(wakeEvent);
+                    } else {
+                        if (DEBUG) Log.d(TAG, "Unpulsing");
+                    }
+                } else if (isPickup) {
                     mDozeHost.setPassiveInterrupt(true);
                     mMachine.wakeUp();
                 } else {
@@ -157,8 +169,6 @@
             final boolean withinVibrationThreshold =
                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
             DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
-        } else if (isWakeLockScreen) {
-            DozeLog.traceWakeLockScreenWakeUp();
         }
     }
 
@@ -184,6 +194,7 @@
     }
 
     private void onWakeScreen(boolean wake) {
+        DozeLog.traceWakeDisplay(wake);
         DozeMachine.State state = mMachine.getState();
         boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
         boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e78951a..201c7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -175,7 +174,7 @@
      * @param builder The slice builder.
      */
     protected void addZenMode(ListBuilder builder) {
-        if (!isDndSuppressingNotifications()) {
+        if (!isDndOn()) {
             return;
         }
         RowBuilder dndBuilder = new RowBuilder(mDndUri)
@@ -187,13 +186,10 @@
     }
 
     /**
-     * Return true if DND is enabled suppressing notifications.
+     * Return true if DND is enabled.
      */
-    protected boolean isDndSuppressingNotifications() {
-        boolean suppressingNotifications = (mZenModeController.getConfig().suppressedVisualEffects
-                & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
-        return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF
-                && suppressingNotifications;
+    protected boolean isDndOn() {
+        return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 5d33ffd..f054345 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -411,13 +411,8 @@
 
         setNextLogTime();
 
-        // This initialization method may be called on a configuration change. Only one set of
-        // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
-        // schedule an ongoing callback.
-        mHandler.removeCallbacks(mUpdateTempCallback);
-
         // We have passed all of the checks, start checking the temp
-        updateTemperatureWarning();
+        mHandler.post(mUpdateTempCallback);
     }
 
     private void showThermalShutdownDialog() {
@@ -448,6 +443,8 @@
 
         logTemperatureStats();
 
+        // Remove any pending callbacks as we only want to enable one
+        mHandler.removeCallbacks(mUpdateTempCallback);
         mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
     }
 
@@ -553,11 +550,7 @@
     // Thermal event received from vendor thermal management subsystem
     private final class ThermalEventListener extends IThermalEventListener.Stub {
         @Override public void notifyThrottling(Temperature temp) {
-            // Trigger an update of the temperature warning.  Only one
-            // callback can be enabled at a time, so remove any existing
-            // callback; updateTemperatureWarning will schedule another one.
-            mHandler.removeCallbacks(mUpdateTempCallback);
-            updateTemperatureWarning();
+            mHandler.post(mUpdateTempCallback);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index d3715d0..65ed889 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -78,8 +78,9 @@
                 if (builder.app != null) {
                     text.setText(builder.app?.applicationName)
                 } else {
-                    text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
-                            builder.appsAndTypes.size)
+                    text.text = context.resources.getQuantityString(
+                            R.plurals.ongoing_privacy_chip_multiple_apps,
+                            builder.appsAndTypes.size, builder.appsAndTypes.size)
                 }
             }
         } else {
@@ -100,9 +101,9 @@
                         context.getString(R.string.ongoing_privacy_chip_content_single_app,
                                 builder.app?.applicationName, typesText)
             } else {
-                contentDescription = context.getString(
-                        R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
-                        builder.appsAndTypes.size, typesText)
+                contentDescription = context.resources.getQuantityString(
+                        R.plurals.ongoing_privacy_chip_content_multiple_apps_single_op,
+                        builder.appsAndTypes.size, builder.appsAndTypes.size, typesText)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 3fa3e8e..268462e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -18,10 +18,14 @@
 
 import android.app.ActivityManager
 import android.app.AppOpsManager
+import android.content.BroadcastReceiver
 import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
 import android.os.Handler
 import android.os.UserHandle
 import android.os.UserManager
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.Dependency
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
@@ -33,25 +37,29 @@
                 AppOpsManager.OP_RECORD_AUDIO,
                 AppOpsManager.OP_COARSE_LOCATION,
                 AppOpsManager.OP_FINE_LOCATION)
+        val intents = listOf(Intent.ACTION_USER_FOREGROUND,
+                Intent.ACTION_MANAGED_PROFILE_ADDED,
+                Intent.ACTION_MANAGED_PROFILE_REMOVED)
+        const val TAG = "PrivacyItemController"
     }
 
     private var privacyList = emptyList<PrivacyItem>()
     private val appOpsController = Dependency.get(AppOpsController::class.java)
     private val userManager = context.getSystemService(UserManager::class.java)
-    private val currentUser = ActivityManager.getCurrentUser()
-    private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+    private var currentUserIds = emptyList<Int>()
     private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
     private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
+    private var listening = false
+
     private val notifyChanges = Runnable {
         callback.privacyChanged(privacyList)
     }
+
     private val updateListAndNotifyChanges = Runnable {
         updatePrivacyList()
         uiHandler.post(notifyChanges)
     }
 
-    private var listening = false
-
     private val cb = object : AppOpsController.Callback {
         override fun onActiveStateChanged(
             code: Int,
@@ -61,12 +69,36 @@
         ) {
             val userId = UserHandle.getUserId(uid)
             if (userId in currentUserIds) {
-                update()
+                update(false)
             }
         }
     }
 
-    private fun update() {
+    @VisibleForTesting
+    internal var userSwitcherReceiver = Receiver()
+        set(value) {
+            context.unregisterReceiver(field)
+            field = value
+            registerReceiver()
+        }
+
+    init {
+        registerReceiver()
+    }
+
+    private fun registerReceiver() {
+        context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
+            intents.forEach {
+                addAction(it)
+            }
+        }, null, null)
+    }
+
+    private fun update(updateUsers: Boolean) {
+        if (updateUsers) {
+            val currentUser = ActivityManager.getCurrentUser()
+            currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+        }
         bgHandler.post(updateListAndNotifyChanges)
     }
 
@@ -75,7 +107,7 @@
         listening = listen
         if (listening) {
             appOpsController.addCallback(OPS, cb)
-            update()
+            update(true)
         } else {
             appOpsController.removeCallback(OPS, cb)
         }
@@ -102,4 +134,12 @@
     interface Callback {
         fun privacyChanged(privacyItems: List<PrivacyItem>)
     }
+
+    internal inner class Receiver : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            if (intent?.action in intents) {
+                update(true)
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 3da6d2e..bc38169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -112,8 +112,7 @@
             return;
         }
 
-        alertEntry.mEntry.row.sendAccessibilityEvent(
-                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        alertEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         if (alert) {
             alertEntry.updateEntry(true /* updatePostTime */);
         }
@@ -186,7 +185,7 @@
         alertEntry.setEntry(entry);
         mAlertEntries.put(entry.key, alertEntry);
         onAlertEntryAdded(alertEntry);
-        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
     /**
@@ -207,7 +206,7 @@
         Entry entry = alertEntry.mEntry;
         mAlertEntries.remove(key);
         onAlertEntryRemoved(alertEntry);
-        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         alertEntry.reset();
         if (mExtendedLifetimeAlertEntries.contains(entry)) {
             if (mNotificationLifetimeFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index f1c0304..a5e7f04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -79,7 +79,7 @@
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setAmbientPulsing(true);
+        entry.setAmbientPulsing(true);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, true);
         }
@@ -88,11 +88,11 @@
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setAmbientPulsing(false);
+        entry.setAmbientPulsing(false);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
         }
-        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+        entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fc1e94a..f045548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -199,7 +199,7 @@
             for (int i = 0; i < N; i++) {
                 final NotificationData.Entry entry = activeNotifications.get(i);
 
-                if (isMediaNotification(entry)) {
+                if (entry.isMediaNotification()) {
                     final MediaSession.Token token =
                             entry.notification.getNotification().extras.getParcelable(
                                     Notification.EXTRA_MEDIA_SESSION);
@@ -336,13 +336,6 @@
         return PlaybackState.STATE_NONE;
     }
 
-    private boolean isMediaNotification(NotificationData.Entry entry) {
-        // TODO: confirm that there's a valid media key
-        return entry.row.getExpandedContentView() != null
-                && entry.row.getExpandedContentView().findViewById(
-                        com.android.internal.R.id.media_actions) != null;
-    }
-
     private void clearCurrentMediaNotificationSession() {
         mMediaMetadata = null;
         if (mMediaController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 8c53cc2..2ee5443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -393,7 +393,7 @@
     }
 
     public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
-        if (entry.row == null || entry.row.isDismissed()) {
+        if (entry.isDismissed()) {
             return false;
         }
         if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -403,7 +403,7 @@
     }
 
     public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
-        if (entry.row == null || entry.row.isDismissed()) {
+        if (entry.isDismissed()) {
             return false;
         }
         if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -532,7 +532,7 @@
 
                 // Ensure the entry hasn't already been removed. This can happen if there is an
                 // inflation exception while updating the remote history
-                if (entry.row == null || entry.row.isRemoved()) {
+                if (entry.isRemoved()) {
                     return;
                 }
 
@@ -570,7 +570,7 @@
 
                 mEntryManager.updateNotification(newSbn, null);
 
-                if (entry.row == null || entry.row.isRemoved()) {
+                if (entry.isRemoved()) {
                     return;
                 }
 
@@ -593,7 +593,7 @@
     protected class RemoteInputActiveExtender extends RemoteInputExtender {
         @Override
         public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
-            if (entry.row == null || entry.row.isDismissed()) {
+            if (entry.isDismissed()) {
                 return false;
             }
             return mRemoteInputController.isRemoteInputActive(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ea67736..daa2fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -51,6 +51,7 @@
 public class NotificationViewHierarchyManager {
     private static final String TAG = "NotificationViewHierarchyManager";
 
+    //TODO: change this top <Entry, List<Entry>>?
     private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
             mTmpChildOrderMap = new HashMap<>();
 
@@ -140,6 +141,7 @@
     /**
      * Updates the visual representation of the notifications.
      */
+    //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
         ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
@@ -148,12 +150,12 @@
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
             NotificationData.Entry ent = activeNotifications.get(i);
-            if (ent.row.isDismissed() || ent.row.isRemoved()) {
+            if (ent.isRowDismissed() || ent.isRowRemoved()) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
                 continue;
             }
-            ent.row.setStatusBarState(mStatusBarStateListener.getCurrentState());
+            ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
             boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
                     && mStatusBarStateListener.getCurrentState() == SHADE;
             if (showAsBubble) {
@@ -175,20 +177,19 @@
             boolean deviceSensitive = devicePublic
                     && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
                     mLockscreenUserManager.getCurrentUserId());
-            ent.row.setSensitive(sensitive, deviceSensitive);
-            ent.row.setNeedsRedaction(needsRedaction);
-            if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
-                ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
-                        ent.row.getStatusBarNotification());
+            ent.getRow().setSensitive(sensitive, deviceSensitive);
+            ent.getRow().setNeedsRedaction(needsRedaction);
+            if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
+                NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
                 List<ExpandableNotificationRow> orderedChildren =
-                        mTmpChildOrderMap.get(summary);
+                        mTmpChildOrderMap.get(summary.getRow());
                 if (orderedChildren == null) {
                     orderedChildren = new ArrayList<>();
-                    mTmpChildOrderMap.put(summary, orderedChildren);
+                    mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
                 }
-                orderedChildren.add(ent.row);
+                orderedChildren.add(ent.getRow());
             } else {
-                toShow.add(ent.row);
+                toShow.add(ent.getRow());
             }
 
         }
@@ -391,19 +392,19 @@
                         && !row.isLowPriority()));
             }
 
-            entry.row.setOnAmbient(getShadeController().isDozing());
+            entry.getRow().setOnAmbient(getShadeController().isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
-                    entry.notification) && !entry.row.isRemoved();
+                    entry.notification) && !entry.isRowRemoved();
             boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
                     .notification);
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
-                    ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+                    NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
-                            summary.getStatusBarNotification()))         {
+                            summary.notification))         {
                         showOnKeyguard = true;
                     }
                 }
@@ -411,16 +412,16 @@
             if (suppressedSummary
                     || mLockscreenUserManager.shouldHideNotifications(userId)
                     || (onKeyguard && !showOnKeyguard)) {
-                entry.row.setVisibility(View.GONE);
+                entry.getRow().setVisibility(View.GONE);
             } else {
-                boolean wasGone = entry.row.getVisibility() == View.GONE;
+                boolean wasGone = entry.getRow().getVisibility() == View.GONE;
                 if (wasGone) {
-                    entry.row.setVisibility(View.VISIBLE);
+                    entry.getRow().setVisibility(View.VISIBLE);
                 }
-                if (!isChildNotification && !entry.row.isRemoved()) {
+                if (!isChildNotification && !entry.getRow().isRemoved()) {
                     if (wasGone) {
                         // notify the scroller of a child addition
-                        mListContainer.generateAddAnimation(entry.row,
+                        mListContainer.generateAddAnimation(entry.getRow(),
                                 !showOnKeyguard /* fromMoreCard */);
                     }
                     visibleNotifications++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 929f43e..e8abcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -250,16 +250,16 @@
         // Make a copy because closing the remote inputs will modify mOpen.
         ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = mOpen.get(i).first.get();
-            if (item != null && item.row != null) {
-                list.add(item);
+            NotificationData.Entry entry = mOpen.get(i).first.get();
+            if (entry != null && entry.rowExists()) {
+                list.add(entry);
             }
         }
 
         for (int i = list.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = list.get(i);
-            if (item.row != null) {
-                item.row.closeRemoteInput();
+            NotificationData.Entry entry = list.get(i);
+            if (entry.rowExists()) {
+                entry.closeRemoteInput();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 758c33a..37bdc1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -41,12 +41,16 @@
         mCallback = callback;
     }
 
-    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
+    /**
+     * Notifies StatusBarService a smart reply is sent.
+     */
+    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+            boolean generatedByAssistant) {
         mCallback.onSmartReplySent(entry, reply);
         mSendingKeys.add(entry.key);
         try {
-            mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
-                    replyIndex);
+            mBarService.onNotificationSmartReplySent(
+                    entry.notification.getKey(), replyIndex, reply, generatedByAssistant);
         } catch (RemoteException e) {
             // Nothing to do, system going down
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index da6d977..d7680b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -64,6 +64,9 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -106,7 +109,6 @@
         public int importance;
         public StatusBarIconView icon;
         public StatusBarIconView expandedIcon;
-        public ExpandableNotificationRow row; // the outer expanded view
         private boolean interruption;
         public boolean autoRedacted; // whether the redacted notification was generated by us
         public int targetSdk;
@@ -119,6 +121,10 @@
         public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
         public CharSequence[] smartReplies = new CharSequence[0];
 
+        private Entry parent; // our parent (if we're in a group)
+        private ArrayList<Entry> children = new ArrayList<Entry>();
+        private ExpandableNotificationRow row; // the outer expanded view
+
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
         private InflationTask mRunningTask = null;
@@ -212,6 +218,24 @@
             }
         }
 
+        public ExpandableNotificationRow getRow() {
+            return row;
+        }
+
+        //TODO: This will go away when we have a way to bind an entry to a row
+        public void setRow(ExpandableNotificationRow row) {
+            this.row = row;
+        }
+
+        @Nullable
+        public List<Entry> getChildren() {
+            if (children.size() <= 0) {
+                return null;
+            }
+
+            return children;
+        }
+
         public void notifyFullScreenIntentLaunched() {
             setInterruption();
             lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -409,6 +433,198 @@
                 initializationTime = time;
             }
         }
+
+        public void sendAccessibilityEvent(int eventType) {
+            if (row != null) {
+                row.sendAccessibilityEvent(eventType);
+            }
+        }
+
+        /**
+         * Used by NotificationMediaManager to determine... things
+         * @return {@code true} if we are a media notification
+         */
+        public boolean isMediaNotification() {
+            if (row == null) return false;
+
+            return row.isMediaRow();
+        }
+
+        /**
+         * We are a top level child if our parent is the list of notifications duh
+         * @return {@code true} if we're a top level notification
+         */
+        public boolean isTopLevelChild() {
+            return row != null && row.isTopLevelChild();
+        }
+
+        public void resetUserExpansion() {
+            if (row != null) row.resetUserExpansion();
+        }
+
+        public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+            if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+        }
+
+        public void setAmbientPulsing(boolean pulsing) {
+            if (row != null) row.setAmbientPulsing(pulsing);
+        }
+
+        public boolean rowExists() {
+            return row != null;
+        }
+
+        public boolean isRowDismissed() {
+            return row != null && row.isDismissed();
+        }
+
+        public boolean isRowRemoved() {
+            return row != null && row.isRemoved();
+        }
+
+        /**
+         * @return {@code true} if the row is null or removed
+         */
+        public boolean isRemoved() {
+            //TODO: recycling invalidates this
+            return row == null || row.isRemoved();
+        }
+
+        /**
+         * @return {@code true} if the row is null or dismissed
+         */
+        public boolean isDismissed() {
+            //TODO: recycling
+            return row == null || row.isDismissed();
+        }
+
+        public boolean isRowPinned() {
+            return row != null && row.isPinned();
+        }
+
+        public void setRowPinned(boolean pinned) {
+            if (row != null) row.setPinned(pinned);
+        }
+
+        public boolean isRowAnimatingAway() {
+            return row != null && row.isHeadsUpAnimatingAway();
+        }
+
+        public boolean isRowHeadsUp() {
+            return row != null && row.isHeadsUp();
+        }
+
+        public void setHeadsUp(boolean shouldHeadsUp) {
+            if (row != null) row.setHeadsUp(shouldHeadsUp);
+        }
+
+        public boolean mustStayOnScreen() {
+            return row != null && row.mustStayOnScreen();
+        }
+
+        public void setHeadsUpIsVisible() {
+            if (row != null) row.setHeadsUpIsVisible();
+        }
+
+        //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+        public ExpandableNotificationRow getHeadsUpAnimationView() {
+            return row;
+        }
+
+        public void setUserLocked(boolean userLocked) {
+            if (row != null) row.setUserLocked(userLocked);
+        }
+
+        public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+            if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+        }
+
+        public void setGroupExpansionChanging(boolean changing) {
+            if (row != null) row.setGroupExpansionChanging(changing);
+        }
+
+        public void notifyHeightChanged(boolean needsAnimation) {
+            if (row != null) row.notifyHeightChanged(needsAnimation);
+        }
+
+        public void closeRemoteInput() {
+            if (row != null) row.closeRemoteInput();
+        }
+
+        public boolean areChildrenExpanded() {
+            return row != null && row.areChildrenExpanded();
+        }
+
+        public boolean keepInParent() {
+            return row != null && row.keepInParent();
+        }
+
+        //TODO: probably less confusing to say "is group fully visible"
+        public boolean isGroupNotFullyVisible() {
+            return row == null || row.isGroupNotFullyVisible();
+        }
+
+        public NotificationGuts getGuts() {
+            if (row != null) return row.getGuts();
+            return null;
+        }
+
+        public boolean hasLowPriorityStateUpdated() {
+            return row != null && row.hasLowPriorityStateUpdated();
+        }
+
+        public void removeRow() {
+            if (row != null) row.setRemoved();
+        }
+
+        public boolean isSummaryWithChildren() {
+            return row != null && row.isSummaryWithChildren();
+        }
+
+        public void setKeepInParent(boolean keep) {
+            if (row != null) row.setKeepInParent(keep);
+        }
+
+        public void onDensityOrFontScaleChanged() {
+            if (row != null) row.onDensityOrFontScaleChanged();
+        }
+
+        public boolean areGutsExposed() {
+            return row != null && row.getGuts().isExposed();
+        }
+
+        public boolean isChildInGroup() {
+            return parent == null;
+        }
+
+        public void setLowPriorityStateUpdated(boolean updated) {
+            if (row != null) row.setLowPriorityStateUpdated(updated);
+        }
+
+        /**
+         * @return Can the underlying notification be cleared? This can be different from whether the
+         *         notification can be dismissed in case notifications are sensitive on the lockscreen.
+         * @see #canViewBeDismissed()
+         */
+        public boolean isClearable() {
+            if (notification == null || !notification.isClearable()) {
+                return false;
+            }
+            if (children.size() > 0) {
+                for (int i = 0; i < children.size(); i++) {
+                    Entry child =  children.get(i);
+                    if (!child.isClearable()) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        public boolean canViewBeDismissed() {
+            if (row == null) return true;
+            return row.canViewBeDismissed();
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 450d34d..e333729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -385,9 +385,9 @@
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
-        if (entry.row != null) {
+        if (entry.rowExists()) {
             entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.row);
+            updateNotification(entry, pmUser, sbn, entry.getRow());
         } else {
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
@@ -543,14 +543,14 @@
                 // Mark as seen immediately
                 setNotificationShown(entry.notification);
             } else {
-                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
             }
         }
         if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
             if (shouldPulse(entry)) {
                 mAmbientPulseManager.showNotification(entry);
             } else {
-                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
             }
         }
     }
@@ -561,20 +561,20 @@
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
         // the list, otherwise we might get leaks.
-        if (!entry.row.isRemoved()) {
+        if (!entry.isRowRemoved()) {
             boolean isNew = mNotificationData.get(entry.key) == null;
             if (isNew) {
                 showAlertingView(entry, inflatedFlags);
                 addEntry(entry);
             } else {
-                if (entry.row.hasLowPriorityStateUpdated()) {
+                if (entry.getRow().hasLowPriorityStateUpdated()) {
                     mVisualStabilityManager.onLowPriorityUpdated(entry);
                     mPresenter.updateNotificationViews();
                 }
                 mGroupAlertTransferHelper.onInflationFinished(entry);
             }
         }
-        entry.row.setLowPriorityStateUpdated(false);
+        entry.setLowPriorityStateUpdated(false);
     }
 
     @Override
@@ -633,9 +633,9 @@
         getMediaManager().onNotificationRemoved(key);
         mForegroundServiceController.removeNotification(entry.notification);
 
-        if (entry.row != null) {
-            entry.row.setRemoved();
-            mListContainer.cleanUpViewState(entry.row);
+        if (entry.rowExists()) {
+            entry.removeRow();
+            mListContainer.cleanUpViewStateForEntry(entry);
         }
 
         // Let's remove the children if this was a summary
@@ -670,19 +670,19 @@
      */
     private void handleGroupSummaryRemoved(String key) {
         NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null && entry.row != null
-                && entry.row.isSummaryWithChildren()) {
-            if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+        if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
+            if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
                 // We don't want to remove children for autobundled notifications as they are not
                 // always cancelled. We only remove them if they were dismissed by the user.
                 return;
             }
-            List<ExpandableNotificationRow> notificationChildren =
-                    entry.row.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow row = notificationChildren.get(i);
-                NotificationData.Entry childEntry = row.getEntry();
-                boolean isForeground = (row.getStatusBarNotification().getNotification().flags
+            List<NotificationData.Entry> childEntries = entry.getChildren();
+            if (childEntries == null) {
+                return;
+            }
+            for (int i = 0; i < childEntries.size(); i++) {
+                NotificationData.Entry childEntry = childEntries.get(i);
+                boolean isForeground = (entry.notification.getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
                         getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -692,10 +692,10 @@
                     // a child we're keeping around for reply!
                     continue;
                 }
-                row.setKeepInParent(true);
+                entry.setKeepInParent(true);
                 // we need to set this state earlier as otherwise we might generate some weird
                 // animations
-                row.setRemoved();
+                entry.removeRow();
             }
         }
     }
@@ -705,15 +705,15 @@
                 mNotificationData.getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
             NotificationData.Entry entry = userNotifications.get(i);
-            boolean exposedGuts = mGutsManager.getExposedGuts() != null
-                    && entry.row.getGuts() == mGutsManager.getExposedGuts();
-            entry.row.onDensityOrFontScaleChanged();
+            entry.onDensityOrFontScaleChanged();
+            boolean exposedGuts = entry.areGutsExposed();
             if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry.row);
+                mGutsManager.onDensityOrFontScaleChanged(entry);
             }
         }
     }
 
+    //TODO: This method associates a row with an entry, but eventually needs to not do that
     protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
@@ -736,8 +736,8 @@
         entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
         entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
 
-        entry.row = row;
-        entry.row.setOnActivatedListener(mPresenter);
+        entry.setRow(row);
+        row.setOnActivatedListener(mPresenter);
 
         boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
                 mNotificationData.getImportance(sbn.getKey()));
@@ -910,7 +910,7 @@
         if (!notification.isClearable()) {
             // The user may have performed a dismiss action on the notification, since it's
             // not clearable we should snap it back.
-            mListContainer.snapViewIfNeeded(entry.row);
+            mListContainer.snapViewIfNeeded(entry);
         }
 
         if (DEBUG) {
@@ -963,11 +963,11 @@
 
             if (NotificationUiAdjustment.needReinflate(
                     oldAdjustments.get(entry.key), newAdjustment)) {
-                if (entry.row != null) {
+                if (entry.rowExists()) {
                     entry.reset();
                     PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                             entry.notification.getUser().getIdentifier());
-                    updateNotification(entry, pmUser, entry.notification, entry.row);
+                    updateNotification(entry, pmUser, entry.notification, entry.getRow());
                 } else {
                     // Once the RowInflaterTask is done, it will pick up the updated entry, so
                     // no-op here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 53ebe74..247c1ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -26,8 +26,8 @@
     /**
      * Returns whether an ExpandableNotificationRow is in a visible location or not.
      *
-     * @param row
+     * @param entry
      * @return true if row is in a visible location
      */
-    boolean isInVisibleLocation(ExpandableNotificationRow row);
+    boolean isInVisibleLocation(NotificationData.Entry entry);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 75613a4..fce7980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -120,7 +120,7 @@
             return true;
         }
         if (mAllowedReorderViews.contains(row)
-                && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
+                && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
             return true;
         }
         return false;
@@ -142,12 +142,12 @@
         if (isHeadsUp) {
             // Heads up notifications should in general be allowed to reorder if they are out of
             // view and stay at the current location if they aren't.
-            mAllowedReorderViews.add(entry.row);
+            mAllowedReorderViews.add(entry.getRow());
         }
     }
 
     public void onLowPriorityUpdated(NotificationData.Entry entry) {
-        mLowPriorityReorderingViews.add(entry.row);
+        mLowPriorityReorderingViews.add(entry.getRow());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 5dfd5d0..87313b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -115,7 +115,7 @@
             for (int i = 0; i < N; i++) {
                 NotificationData.Entry entry = activeNotifications.get(i);
                 String key = entry.notification.getKey();
-                boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+                boolean isVisible = mListContainer.isInVisibleLocation(entry);
                 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
                 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
                 if (isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b6d99b2..23bfdad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -104,6 +104,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.StackScrollState;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -1411,16 +1412,16 @@
 
     public void performDismiss(boolean fromAccessibility) {
         if (isOnlyChildInGroup()) {
-            ExpandableNotificationRow groupSummary =
+            NotificationData.Entry groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
                 // If this is the only child in the group, dismiss the group, but don't try to show
                 // the blocking helper affordance!
-                groupSummary.performDismiss(fromAccessibility);
+                groupSummary.getRow().performDismiss(fromAccessibility);
             }
         }
         setDismissed(fromAccessibility);
-        if (isClearable()) {
+        if (mEntry.isClearable()) {
             // TODO: track dismiss sentiment
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
@@ -2244,28 +2245,6 @@
         setRippleAllowed(allowed);
     }
 
-    /**
-     * @return Can the underlying notification be cleared? This can be different from whether the
-     *         notification can be dismissed in case notifications are sensitive on the lockscreen.
-     * @see #canViewBeDismissed()
-     */
-    public boolean isClearable() {
-        if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
-            return false;
-        }
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                if (!child.isClearable()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
     @Override
     public int getIntrinsicHeight() {
         if (isShownAsBubble()) {
@@ -2533,10 +2512,10 @@
     /**
      * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
      *         otherwise some state might not be updated. To request about the general clearability
-     *         see {@link #isClearable()}.
+     *         see {@link NotificationData.Entry#isClearable()}.
      */
     public boolean canViewBeDismissed() {
-        return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+        return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
     }
 
     private boolean shouldShowPublic() {
@@ -3038,6 +3017,21 @@
         return mOnAmbient;
     }
 
+    //TODO: this logic can't depend on layout if we are recycling!
+    public boolean isMediaRow() {
+        return getExpandedContentView() != null
+                && getExpandedContentView().findViewById(
+                com.android.internal.R.id.media_actions) != null;
+    }
+
+    public boolean isTopLevelChild() {
+        return getParent() instanceof NotificationStackScrollLayout;
+    }
+
+    public boolean isGroupNotFullyVisible() {
+        return getClipTopAmount() > 0 || getTranslationY() < 0;
+    }
+
     public void setAboveShelf(boolean aboveShelf) {
         boolean wasAboveShelf = isAboveShelf();
         mAboveShelf = aboveShelf;
@@ -3145,8 +3139,10 @@
         pw.print(", alpha: " + getAlpha());
         pw.print(", translation: " + getTranslation());
         pw.print(", removed: " + isRemoved());
-        pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+        NotificationContentView showingLayout = getShowingLayout();
+        pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
         pw.println();
+        showingLayout.dump(fd, pw, args);
         pw.print("    ");
         if (mNotificationViewState != null) {
             mNotificationViewState.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index bb9a341..a4fdc08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -55,6 +55,9 @@
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -1227,17 +1230,18 @@
         mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateAllSingleLineViews();
+        ExpandableNotificationRow row = entry.getRow();
         if (mContractedChild != null) {
-            mContractedWrapper.onContentUpdated(entry.row);
+            mContractedWrapper.onContentUpdated(row);
         }
         if (mExpandedChild != null) {
-            mExpandedWrapper.onContentUpdated(entry.row);
+            mExpandedWrapper.onContentUpdated(row);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.onContentUpdated(entry.row);
+            mHeadsUpWrapper.onContentUpdated(row);
         }
         if (mAmbientChild != null) {
-            mAmbientWrapper.onContentUpdated(entry.row);
+            mAmbientWrapper.onContentUpdated(row);
         }
         applyRemoteInputAndSmartReply(entry);
         updateLegacy();
@@ -1287,10 +1291,10 @@
             return;
         }
 
-        SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
-                mSmartReplyConstants, entry);
+        SmartRepliesAndActions smartRepliesAndActions =
+                chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
 
-        applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+        applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
         applySmartReplyView(smartRepliesAndActions, entry);
     }
 
@@ -1317,58 +1321,47 @@
         boolean appGeneratedSmartRepliesExist =
                 enableAppGeneratedSmartReplies
                         && remoteInputActionPair != null
-                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
+                        && remoteInputActionPair.second.actionIntent != null;
 
         List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
         boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
 
+        SmartReplyView.SmartReplies smartReplies = null;
+        SmartReplyView.SmartActions smartActions = null;
         if (appGeneratedSmartRepliesExist) {
-            return new SmartRepliesAndActions(remoteInputActionPair.first,
-                    remoteInputActionPair.second.actionIntent,
+            smartReplies = new SmartReplyView.SmartReplies(
                     remoteInputActionPair.first.getChoices(),
-                    appGeneratedSmartActions,
-                    freeformRemoteInputActionPair);
-        } else if (appGeneratedSmartActionsExist) {
-            return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
-                    freeformRemoteInputActionPair);
-        } else if (!ArrayUtils.isEmpty(entry.smartReplies)
-                && freeformRemoteInputActionPair != null
-                && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
-            // App didn't generate anything, use NAS-generated replies and actions
-            return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
-                    freeformRemoteInputActionPair.second.actionIntent,
-                    entry.smartReplies,
-                    entry.systemGeneratedSmartActions,
-                    freeformRemoteInputActionPair);
+                    remoteInputActionPair.first,
+                    remoteInputActionPair.second.actionIntent,
+                    false /* fromAssistant */);
         }
-        // App didn't generate anything, and there are no NAS-generated smart replies.
-        return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
-                freeformRemoteInputActionPair);
-    }
-
-    @VisibleForTesting
-    static class SmartRepliesAndActions {
-        public final RemoteInput remoteInputWithChoices;
-        public final PendingIntent pendingIntentForSmartReplies;
-        public final CharSequence[] smartReplies;
-        public final List<Notification.Action> smartActions;
-        public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
-
-        SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
-                CharSequence[] choices, List<Notification.Action> smartActions,
-                Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
-            this.remoteInputWithChoices = remoteInput;
-            this.pendingIntentForSmartReplies = pendingIntent;
-            this.smartReplies = choices;
-            this.smartActions = smartActions;
-            this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
+        if (appGeneratedSmartActionsExist) {
+            smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
+                    false /* fromAssistant */);
         }
-
-        boolean smartRepliesExist() {
-            return remoteInputWithChoices != null
-                    && pendingIntentForSmartReplies != null
-                    && !ArrayUtils.isEmpty(smartReplies);
+        // Apps didn't provide any smart replies / actions, use those from NAS (if any).
+        if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
+            boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+                    && freeformRemoteInputActionPair != null
+                    && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
+                    && freeformRemoteInputActionPair.second.actionIntent != null;
+            if (useGeneratedReplies) {
+                smartReplies = new SmartReplyView.SmartReplies(
+                        entry.smartReplies,
+                        freeformRemoteInputActionPair.first,
+                        freeformRemoteInputActionPair.second.actionIntent,
+                        true /* fromAssistant */);
+            }
+            boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
+                    && notification.getAllowSystemGeneratedContextualActions();
+            if (useSmartActions) {
+                smartActions = new SmartReplyView.SmartActions(
+                        entry.systemGeneratedSmartActions, true /* fromAssistant */);
+            }
         }
+        return new SmartRepliesAndActions(
+                smartReplies, smartActions, freeformRemoteInputActionPair != null);
     }
 
     private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1475,12 +1468,9 @@
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
                     applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
-            if (mExpandedSmartReplyView != null
-                    && smartRepliesAndActions.remoteInputWithChoices != null
-                    && smartRepliesAndActions.smartReplies != null
-                    && smartRepliesAndActions.smartReplies.length > 0) {
-                mSmartReplyController.smartRepliesAdded(entry,
-                        smartRepliesAndActions.smartReplies.length);
+            if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
+                mSmartReplyController.smartRepliesAdded(
+                        entry, smartRepliesAndActions.smartReplies.choices.length);
             }
         }
     }
@@ -1494,8 +1484,8 @@
         }
         LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
         // If there are no smart replies and no smart actions - early out.
-        if (!smartRepliesAndActions.smartRepliesExist()
-                && smartRepliesAndActions.smartActions.isEmpty()) {
+        if (smartRepliesAndActions.smartReplies == null
+                && smartRepliesAndActions.smartActions == null) {
             smartReplyContainer.setVisibility(View.GONE);
             return null;
         }
@@ -1525,10 +1515,13 @@
         }
         if (smartReplyView != null) {
             smartReplyView.resetSmartSuggestions(smartReplyContainer);
-            smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
-                    smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
-                    entry, smartRepliesAndActions.smartReplies);
-            smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+            if (smartRepliesAndActions.smartReplies != null) {
+                smartReplyView.addRepliesFromRemoteInput(
+                        smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
+            }
+            if (smartRepliesAndActions.smartActions != null) {
+                smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+            }
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
         return smartReplyView;
@@ -1928,4 +1921,41 @@
             mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
         }
     }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("    ");
+        pw.print("contentView visibility: " + getVisibility());
+        pw.print(", alpha: " + getAlpha());
+        pw.print(", clipBounds: " + getClipBounds());
+        pw.print(", contentHeight: " + mContentHeight);
+        pw.print(", visibleType: " + mVisibleType);
+        View view = getViewForVisibleType(mVisibleType);
+        pw.print(", visibleView ");
+        if (view != null) {
+            pw.print(" visibility: " + view.getVisibility());
+            pw.print(", alpha: " + view.getAlpha());
+            pw.print(", clipBounds: " + view.getClipBounds());
+        } else {
+            pw.print("null");
+        }
+        pw.println();
+    }
+
+    @VisibleForTesting
+    static class SmartRepliesAndActions {
+        @Nullable
+        public final SmartReplyView.SmartReplies smartReplies;
+        @Nullable
+        public final SmartReplyView.SmartActions smartActions;
+        public final boolean hasFreeformRemoteInput;
+
+        SmartRepliesAndActions(
+                @Nullable SmartReplyView.SmartReplies smartReplies,
+                @Nullable SmartReplyView.SmartActions smartActions,
+                boolean hasFreeformRemoteInput) {
+            this.smartReplies = smartReplies;
+            this.smartActions = smartActions;
+            this.hasFreeformRemoteInput = hasFreeformRemoteInput;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 37bf06e..7895a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -86,7 +86,6 @@
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
 
     // which notification is currently being longpress-examined by the user
-    private final IStatusBarService mBarService;
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
     private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
@@ -103,8 +102,6 @@
 
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -116,9 +113,9 @@
         mOnSettingsClickListener = onSettingsClick;
     }
 
-    public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
-        setExposedGuts(row.getGuts());
-        bindGuts(row);
+    public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+        setExposedGuts(entry.getGuts());
+        bindGuts(entry.getRow());
     }
 
     /**
@@ -441,8 +438,8 @@
     public boolean shouldExtendLifetime(NotificationData.Entry entry) {
         return entry != null
                 &&(mNotificationGutsExposed != null
-                    && entry.row.getGuts() != null
-                    && mNotificationGutsExposed == entry.row.getGuts()
+                    && entry.getGuts() != null
+                    && mNotificationGutsExposed == entry.getGuts()
                     && !mNotificationGutsExposed.isLeavebehind());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 3a7091b..0d36d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -92,6 +92,7 @@
     private String mPackageName;
     private String mAppName;
     private int mAppUid;
+    private String mDelegatePkg;
     private int mNumUniqueChannelsInRow;
     private NotificationChannel mSingleNotificationChannel;
     private int mStartingChannelImportance;
@@ -235,6 +236,7 @@
                 (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
         mIsForBlockingHelper = isForBlockingHelper;
         mAppUid = mSbn.getUid();
+        mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
         mIsNoisy = isNoisy;
 
@@ -281,26 +283,8 @@
         ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
         ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
 
-        // Set group information if this channel has an associated group.
-        CharSequence groupName = null;
-        if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
-            final NotificationChannelGroup notificationChannelGroup =
-                    mINotificationManager.getNotificationChannelGroupForPackage(
-                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
-            if (notificationChannelGroup != null) {
-                groupName = notificationChannelGroup.getName();
-            }
-        }
-        TextView groupNameView = findViewById(R.id.group_name);
-        TextView groupDividerView = findViewById(R.id.pkg_group_divider);
-        if (groupName != null) {
-            groupNameView.setText(groupName);
-            groupNameView.setVisibility(View.VISIBLE);
-            groupDividerView.setVisibility(View.VISIBLE);
-        } else {
-            groupNameView.setVisibility(View.GONE);
-            groupDividerView.setVisibility(View.GONE);
-        }
+        // Delegate
+        bindDelegate();
 
         // Settings button.
         final View settingsButton = findViewById(R.id.info);
@@ -320,9 +304,10 @@
         }
     }
 
-    private void bindPrompt() {
+    private void bindPrompt() throws RemoteException {
         final TextView blockPrompt = findViewById(R.id.block_prompt);
         bindName();
+        bindGroup();
         if (mIsNonblockable) {
             blockPrompt.setText(R.string.notification_unblockable_desc);
         } else {
@@ -345,6 +330,60 @@
         }
     }
 
+    private void bindDelegate() {
+        TextView delegateView = findViewById(R.id.delegate_name);
+        TextView dividerView = findViewById(R.id.pkg_divider);
+
+        CharSequence delegatePkg = null;
+        if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+            // this notification was posted by a delegate!
+            ApplicationInfo info;
+            try {
+                info = mPm.getApplicationInfo(
+                        mDelegatePkg,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                | PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+                if (info != null) {
+                    delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
+                }
+            } catch (PackageManager.NameNotFoundException e) { }
+        }
+        if (delegatePkg != null) {
+            delegateView.setText(mContext.getResources().getString(
+                    R.string.notification_delegate_header, delegatePkg));
+            delegateView.setVisibility(View.VISIBLE);
+            dividerView.setVisibility(View.VISIBLE);
+        } else {
+            delegateView.setVisibility(View.GONE);
+            dividerView.setVisibility(View.GONE);
+        }
+    }
+
+    private void bindGroup() throws RemoteException {
+        // Set group information if this channel has an associated group.
+        CharSequence groupName = null;
+        if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+            final NotificationChannelGroup notificationChannelGroup =
+                    mINotificationManager.getNotificationChannelGroupForPackage(
+                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+            if (notificationChannelGroup != null) {
+                groupName = notificationChannelGroup.getName();
+            }
+        }
+        TextView groupNameView = findViewById(R.id.group_name);
+        TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+        if (groupName != null) {
+            groupNameView.setText(groupName);
+            groupNameView.setVisibility(View.VISIBLE);
+            groupDividerView.setVisibility(View.VISIBLE);
+        } else {
+            groupNameView.setVisibility(View.GONE);
+            groupDividerView.setVisibility(View.GONE);
+        }
+    }
+
     @VisibleForTesting
     void logBlockingHelperCounter(String counterTag) {
         if (mIsForBlockingHelper) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 4d100a4..6de4fc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -118,9 +118,9 @@
     /**
      * Handle snapping a non-dismissable row back if the user tried to dismiss it.
      *
-     * @param row row to snap back
+     * @param entry the entry whose row needs to snap back
      */
-    void snapViewIfNeeded(ExpandableNotificationRow row);
+    void snapViewIfNeeded(NotificationData.Entry entry);
 
     /**
      * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
@@ -149,9 +149,9 @@
      * Called when a notification is removed from the shade. This cleans up the state for a
      * given view.
      *
-     * @param view view to clean up view state for
+     * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
      */
-    void cleanUpViewState(View view);
+    void cleanUpViewStateForEntry(NotificationData.Entry entry);
 
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index c867a41..27838a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -21,6 +21,7 @@
 
 import android.view.View;
 
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -50,13 +51,13 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        updateView(headsUp, false /* animate */);
+    public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+        updateView(headsUp.getRow(), false /* animate */);
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
-        updateView(headsUp, true /* animate */);
+    public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+        updateView(headsUp.getRow(), true /* animate */);
     }
 
     public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ecd0d98..faccff3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -600,12 +600,12 @@
             public void setRemoteInputActive(NotificationData.Entry entry,
                     boolean remoteInputActive) {
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                entry.row.notifyHeightChanged(true /* needsAnimation */);
+                entry.notifyHeightChanged(true /* needsAnimation */);
                 updateFooter();
             }
 
             public void lockScrollTo(NotificationData.Entry entry) {
-                NotificationStackScrollLayout.this.lockScrollTo(entry.row);
+                NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
             }
 
             public void requestDisallowLongPressAndDismiss() {
@@ -897,7 +897,8 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+    public boolean isInVisibleLocation(NotificationData.Entry entry) {
+        ExpandableNotificationRow row = entry.getRow();
         ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
         if (childViewState == null) {
             return false;
@@ -1213,12 +1214,12 @@
         if (topEntry == null) {
             return 0;
         }
-        ExpandableNotificationRow row = topEntry.row;
+        ExpandableNotificationRow row = topEntry.getRow();
         if (row.isChildInGroup()) {
-            final ExpandableNotificationRow groupSummary
+            final NotificationData.Entry groupSummary
                     = mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null) {
-                row = groupSummary;
+                row = groupSummary.getRow();
             }
         }
         return row.getPinnedHeadsUpHeight();
@@ -1390,11 +1391,12 @@
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+                    NotificationData.Entry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().row != row
+                            && mHeadsUpManager.getTopEntry().getRow() != row
                             && mGroupManager.getGroupSummary(
-                            mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
-                            != row) {
+                                mHeadsUpManager.getTopEntry().notification)
+                            != entry) {
                         continue;
                     }
                     return row.getViewAtPosition(touchY - childTop);
@@ -1524,7 +1526,8 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void snapViewIfNeeded(ExpandableNotificationRow child) {
+    public void snapViewIfNeeded(NotificationData.Entry entry) {
+        ExpandableNotificationRow child = entry.getRow();
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
         float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
@@ -2514,7 +2517,8 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
-    public void cleanUpViewState(View child) {
+    public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+        View child = entry.getRow();
         if (child == mSwipeHelper.getTranslatingParentView()) {
             mSwipeHelper.clearTranslatingParentView();
         }
@@ -2644,9 +2648,9 @@
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            ExpandableNotificationRow groupSummary =
+            NotificationData.Entry groupSummary =
                     mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null && groupSummary != row) {
+            if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
             }
         }
@@ -4662,6 +4666,11 @@
         mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
+    public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+        ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+        generateHeadsUpAnimation(row, isHeadsUp);
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
         if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
@@ -5692,7 +5701,7 @@
                         && (parent.areGutsExposed()
                         || mSwipeHelper.getExposedMenuView() == parent
                         || (parent.getNotificationChildren().size() == 1
-                        && parent.isClearable()))) {
+                        && parent.getEntry().isClearable()))) {
                     // In this case the group is expanded and showing the menu for the
                     // group, further interaction should apply to the group, not any
                     // child notifications so we use the parent of the child. We also do the same
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 40f9f45..3c8cad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -143,9 +143,9 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
         updateTopEntry();
-        updateHeader(headsUp.getEntry());
+        updateHeader(entry);
     }
 
     /** To count the distance from the window right boundary to scroller right boundary. The
@@ -298,9 +298,9 @@
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
         updateTopEntry();
-        updateHeader(headsUp.getEntry());
+        updateHeader(entry);
     }
 
     public void setExpandedHeight(float expandedHeight, float appearFraction) {
@@ -339,7 +339,7 @@
     }
 
     public void updateHeader(NotificationData.Entry entry) {
-        ExpandableNotificationRow row = entry.row;
+        ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
         if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
             headerVisibleAmount = mExpandFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 00d6b14..9faada0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -304,18 +304,19 @@
             return;
         }
         if (hasPinnedHeadsUp()) {
-            ExpandableNotificationRow topEntry = getTopEntry().row;
+            NotificationData.Entry topEntry = getTopEntry();
             if (topEntry.isChildInGroup()) {
-                final ExpandableNotificationRow groupSummary
-                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+                final NotificationData.Entry groupSummary
+                        = mGroupManager.getGroupSummary(topEntry.notification);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
                 }
             }
-            topEntry.getLocationOnScreen(mTmpTwoArray);
+            ExpandableNotificationRow topRow = topEntry.getRow();
+            topRow.getLocationOnScreen(mTmpTwoArray);
             int minX = mTmpTwoArray[0];
-            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int height = topEntry.getIntrinsicHeight();
+            int maxX = mTmpTwoArray[0] + topRow.getWidth();
+            int height = topRow.getIntrinsicHeight();
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index be4df45..9c1c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -84,8 +84,8 @@
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
                     NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
-                    if (topEntry != null && topEntry.row.isPinned()) {
-                        mPickedChild = topEntry.row;
+                    if (topEntry != null && topEntry.isRowPinned()) {
+                        mPickedChild = topEntry.getRow();
                         mTouchingHeadsUpView = true;
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 0cf1b3d..8657003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -56,7 +56,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
-import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -863,16 +862,9 @@
 
     public static View create(Context context, FragmentListener listener) {
         final int displayId = context.getDisplay().getDisplayId();
-        final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-        final int height = isDefaultDisplay
-                ? LayoutParams.MATCH_PARENT
-                : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, height,
-                // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
-                isDefaultDisplay
-                        ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
-                        : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -884,10 +876,6 @@
         lp.setTitle("NavigationBar" + displayId);
         lp.accessibilityTitle = context.getString(R.string.nav_bar);
         lp.windowAnimations = 0;
-        if (!isDefaultDisplay) {
-            lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-            lp.gravity = Gravity.BOTTOM;
-        }
 
         View navigationBarView = LayoutInflater.from(context).inflate(
                 R.layout.navigation_bar_window, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index c74514e..3e31fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -198,7 +198,7 @@
                 alertNotificationWhenPossible(entry, getActiveAlertManager());
             } else {
                 // The transfer is no longer valid. Free the content.
-                entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+                entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
             }
         }
     }
@@ -299,9 +299,9 @@
 
         Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
         if (child != null) {
-            if (child.row.keepInParent()
-                    || child.row.isRemoved()
-                    || child.row.isDismissed()) {
+            if (child.getRow().keepInParent()
+                    || child.isRowRemoved()
+                    || child.isRowDismissed()) {
                 // The notification is actually already removed. No need to alert it.
                 return;
             }
@@ -390,10 +390,10 @@
     private void alertNotificationWhenPossible(@NonNull Entry entry,
             @NonNull AlertingNotificationManager alertManager) {
         @InflationFlag int contentFlag = alertManager.getContentFlag();
-        if (!entry.row.isInflationFlagSet(contentFlag)) {
+        if (!entry.getRow().isInflationFlagSet(contentFlag)) {
             mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
-            entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
-            entry.row.inflateViews();
+            entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+            entry.getRow().inflateViews();
             return;
         }
         if (alertManager.isAlerting(entry.key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8ceabf8..448b5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -87,8 +87,7 @@
         group.expanded = expanded;
         if (group.summary != null) {
             for (OnGroupChangeListener listener : mListeners) {
-                listener.onGroupExpansionChanged(group.summary.row,
-                        expanded);
+                listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
             }
         }
     }
@@ -133,7 +132,7 @@
     }
 
     public void onEntryAdded(final NotificationData.Entry added) {
-        if (added.row.isRemoved()) {
+        if (added.isRowRemoved()) {
             added.setDebugThrowable(new Throwable());
         }
         final StatusBarNotification sbn = added.notification;
@@ -152,17 +151,17 @@
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
-                        + "existing removed: " + existing.row.isRemoved()
+                        + "existing removed: " + existing.isRowRemoved()
                         + (existingThrowable != null
                                 ? Log.getStackTraceString(existingThrowable) + "\n": "")
-                        + " added removed" + added.row.isRemoved()
+                        + " added removed" + added.isRowRemoved()
                         , new Throwable());
             }
             group.children.put(added.key, added);
             updateSuppression(group);
         } else {
             group.summary = added;
-            group.expanded = added.row.areChildrenExpanded();
+            group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
                 ArrayList<NotificationData.Entry> childrenCopy
@@ -263,9 +262,9 @@
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        ExpandableNotificationRow logicalGroupSummary = getLogicalGroupSummary(sbn);
+        NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
         return logicalGroupSummary != null
-                && !logicalGroupSummary.getStatusBarNotification().equals(sbn);
+                && !logicalGroupSummary.notification.equals(sbn);
     }
 
     private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,7 +338,7 @@
      * Get the summary of a specified status bar notification. For isolated notification this return
      * itself.
      */
-    public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
+    public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(getGroupKey(sbn));
     }
 
@@ -348,16 +347,17 @@
      * but the logical summary, i.e when a child is isolated, it still returns the summary as if
      * it wasn't isolated.
      */
-    public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
+    public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(sbn.getGroupKey());
     }
 
     @Nullable
-    private ExpandableNotificationRow getGroupSummary(String groupKey) {
+    private NotificationData.Entry getGroupSummary(String groupKey) {
         NotificationGroup group = mGroupMap.get(groupKey);
+        //TODO: see if this can become an Entry
         return group == null ? null
                 : group.summary == null ? null
-                        : group.summary.row;
+                        : group.summary;
     }
 
     /**
@@ -438,11 +438,11 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
     }
 
     @Override
@@ -533,8 +533,7 @@
 
     private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
         return notificationGroup.summary == null
-                || notificationGroup.summary.row.getClipTopAmount() > 0
-                || notificationGroup.summary.row.getTranslationY() < 0;
+                || notificationGroup.summary.isGroupNotFullyVisible();
     }
 
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 184766c..2d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -192,13 +192,13 @@
                 && !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
             return false;
         }
-        if (!StatusBar.isTopLevelChild(entry)) {
+        if (!entry.isTopLevelChild()) {
             return false;
         }
-        if (entry.row.getVisibility() == View.GONE) {
+        if (entry.getRow().getVisibility() == View.GONE) {
             return false;
         }
-        if (entry.row.isDismissed() && hideDismissed) {
+        if (entry.isRowDismissed() && hideDismissed) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 851e6d0..33d176a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2500,25 +2500,26 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
+        mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
 
         // When we're unpinning the notification via active edge they remain heads-upped,
         // we need to make sure that an animation happens in this case, otherwise the notification
         // will stick to the top without any interaction.
-        if (isFullyCollapsed() && headsUp.isHeadsUp()) {
-            mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
-            headsUp.setHeadsUpIsVisible();
+        if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+            mNotificationStackScroller.generateHeadsUpAnimation(
+                    entry.getHeadsUpAnimationView(), false);
+            entry.setHeadsUpIsVisible();
         }
     }
 
     @Override
     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+        mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
     @Override
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 bdddf5b..05f8f18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -484,7 +484,7 @@
 
     private Runnable mLaunchTransitionEndRunnable;
     protected boolean mLaunchTransitionFadingAway;
-    private ExpandableNotificationRow mDraggedDownRow;
+    private NotificationData.Entry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -1265,10 +1265,6 @@
         mQSPanel.clickTile(tile);
     }
 
-    public static boolean isTopLevelChild(Entry entry) {
-        return entry.row.getParent() instanceof NotificationStackScrollLayout;
-    }
-
     public boolean areNotificationsHidden() {
         return mZenController.areNotificationsHiddenInShade();
     }
@@ -1491,12 +1487,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
         dismissVolumeDialog();
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
     }
 
     @Override
@@ -2605,9 +2601,7 @@
         final int notificationCount = activeNotifications.size();
         for (int i = 0; i < notificationCount; i++) {
             NotificationData.Entry entry = activeNotifications.get(i);
-            if (entry.row != null) {
-                entry.row.resetUserExpansion();
-            }
+            entry.resetUserExpansion();
         }
     }
 
@@ -3038,10 +3032,10 @@
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
         updatePanelExpansionForKeyguard();
-        if (mDraggedDownRow != null) {
-            mDraggedDownRow.setUserLocked(false);
-            mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
-            mDraggedDownRow = null;
+        if (mDraggedDownEntry != null) {
+            mDraggedDownEntry.setUserLocked(false);
+            mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
+            mDraggedDownEntry = null;
         }
     }
 
@@ -3181,9 +3175,9 @@
             }
             long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
             mNotificationPanel.animateToFullShade(delay);
-            if (mDraggedDownRow != null) {
-                mDraggedDownRow.setUserLocked(false);
-                mDraggedDownRow = null;
+            if (mDraggedDownEntry != null) {
+                mDraggedDownEntry.setUserLocked(false);
+                mDraggedDownEntry = null;
             }
 
             // TODO(115978725): Support animations on external nav bars.
@@ -3575,14 +3569,15 @@
 
         int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
+        NotificationData.Entry entry = null;
         if (expandView instanceof ExpandableNotificationRow) {
-            row = (ExpandableNotificationRow) expandView;
-            row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+            entry = ((ExpandableNotificationRow) expandView).getEntry();
+            entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
-            row.setGroupExpansionChanging(true);
-            if (row.getStatusBarNotification() != null) {
-                userId = row.getStatusBarNotification().getUserId();
+            entry.setGroupExpansionChanging(true);
+            if (entry.notification != null) {
+                userId = entry.notification.getUserId();
             }
         }
         boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
@@ -3592,7 +3587,7 @@
         if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             showBouncerIfKeyguard();
-            mDraggedDownRow = row;
+            mDraggedDownEntry = entry;
             mPendingRemoteInputView = null;
         } else {
             mNotificationPanel.animateToFullShade(0 /* delay */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index edfc049..588c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -418,7 +418,7 @@
             StatusBarNotification parentToCancel = null;
             if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
                 StatusBarNotification summarySbn =
-                        mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+                        mGroupManager.getLogicalGroupSummary(sbn).notification;
                 if (shouldAutoCancel(summarySbn)) {
                     parentToCancel = summarySbn;
                 }
@@ -591,7 +591,7 @@
     public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
-            mShadeController.goToLockedShade(clickedEntry.row);
+            mShadeController.goToLockedShade(clickedEntry.getRow());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fdab616..e7280643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -123,15 +123,15 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setEntryPinned: " + isPinned);
         }
-        ExpandableNotificationRow row = headsUpEntry.mEntry.row;
-        if (row.isPinned() != isPinned) {
-            row.setPinned(isPinned);
+        NotificationData.Entry entry = headsUpEntry.mEntry;
+        if (entry.isRowPinned() != isPinned) {
+            entry.setRowPinned(isPinned);
             updatePinnedMode();
             for (OnHeadsUpChangedListener listener : mListeners) {
                 if (isPinned) {
-                    listener.onHeadsUpPinned(row);
+                    listener.onHeadsUpPinned(entry);
                 } else {
-                    listener.onHeadsUpUnPinned(row);
+                    listener.onHeadsUpUnPinned(entry);
                 }
             }
         }
@@ -144,7 +144,7 @@
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setHeadsUp(true);
+        entry.setHeadsUp(true);
         setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, true);
@@ -154,12 +154,12 @@
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setHeadsUp(false);
+        entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
-        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+        entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
     }
 
     protected void updatePinnedMode() {
@@ -282,7 +282,7 @@
     private boolean hasPinnedNotificationInternal() {
         for (String key : mAlertEntries.keySet()) {
             AlertEntry entry = getHeadsUpEntry(key);
-            if (entry.mEntry.row.isPinned()) {
+            if (entry.mEntry.isRowPinned()) {
                 return true;
             }
         }
@@ -302,10 +302,9 @@
 
             // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
             // on the screen.
-            if (userUnPinned && entry.mEntry != null && entry.mEntry.row != null) {
-                ExpandableNotificationRow row = entry.mEntry.row;
-                if (row.mustStayOnScreen()) {
-                    row.setHeadsUpIsVisible();
+            if (userUnPinned && entry.mEntry != null) {
+                if (entry.mEntry.mustStayOnScreen()) {
+                    entry.mEntry.setHeadsUpIsVisible();
                 }
             }
         }
@@ -341,7 +340,7 @@
      */
     public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
-        if (headsUpEntry != null && entry.row.isPinned()) {
+        if (headsUpEntry != null && entry.isRowPinned()) {
             headsUpEntry.setExpanded(expanded);
         }
     }
@@ -365,15 +364,15 @@
 
         @Override
         protected boolean isSticky() {
-            return (mEntry.row.isPinned() && expanded)
+            return (mEntry.isRowPinned() && expanded)
                     || remoteInputActive || hasFullScreenIntent(mEntry);
         }
 
         @Override
         public int compareTo(@NonNull AlertEntry alertEntry) {
             HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
-            boolean isPinned = mEntry.row.isPinned();
-            boolean otherPinned = headsUpEntry.mEntry.row.isPinned();
+            boolean isPinned = mEntry.isRowPinned();
+            boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
             if (isPinned && !otherPinned) {
                 return -1;
             } else if (!isPinned && otherPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index d434768..7ad547a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -33,12 +33,12 @@
     /**
      * A notification was just pinned to the top.
      */
-    default void onHeadsUpPinned(ExpandableNotificationRow headsUp) {}
+    default void onHeadsUpPinned(NotificationData.Entry entry) {}
 
     /**
      * A notification was just unpinned from the top.
      */
-    default void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {}
+    default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
 
     /**
      * A notification just became a heads up or turned back to its normal state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a485fa8..866015e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -245,7 +245,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        if (mEntry.row.isChangingPosition()) {
+        if (mEntry.getRow().isChangingPosition()) {
             if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
                 mEditText.requestFocus();
             }
@@ -255,7 +255,7 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
+        if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
             return;
         }
         mController.removeRemoteInput(mEntry, mToken);
@@ -495,7 +495,7 @@
         }
 
         private void defocusIfNeeded(boolean animate) {
-            if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
+            if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
                     || isTemporarilyDetached()) {
                 if (isTemporarilyDetached()) {
                     // We might get reattached but then the other one of HUN / expanded might steal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 0186683..88ff078 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.ColorInt;
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -189,15 +190,13 @@
      * into the notification are shown.
      */
     public void addRepliesFromRemoteInput(
-            RemoteInput remoteInput, PendingIntent pendingIntent,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
-            CharSequence[] choices) {
-        if (remoteInput != null && pendingIntent != null) {
-            if (choices != null) {
-                for (int i = 0; i < choices.length; ++i) {
+            SmartReplies smartReplies,
+            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+        if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
+            if (smartReplies.choices != null) {
+                for (int i = 0; i < smartReplies.choices.length; ++i) {
                     Button replyButton = inflateReplyButton(
-                            getContext(), this, i, choices[i], remoteInput, pendingIntent,
-                            smartReplyController, entry);
+                            getContext(), this, i, smartReplies, smartReplyController, entry);
                     addView(replyButton);
                 }
             }
@@ -209,10 +208,10 @@
      * Add smart actions to be shown next to smart replies. Only the actions that fit into the
      * notification are shown.
      */
-    public void addSmartActions(List<Notification.Action> smartActions) {
-        int numSmartActions = smartActions.size();
+    public void addSmartActions(SmartActions smartActions) {
+        int numSmartActions = smartActions.actions.size();
         for (int n = 0; n < numSmartActions; n++) {
-            Notification.Action action = smartActions.get(n);
+            Notification.Action action = smartActions.actions.get(n);
             if (action.actionIntent != null) {
                 Button actionButton = inflateActionButton(getContext(), this, action);
                 addView(actionButton);
@@ -228,22 +227,25 @@
 
     @VisibleForTesting
     Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
-            CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
-            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+            SmartReplies smartReplies, SmartReplyController smartReplyController,
+            NotificationData.Entry entry) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
+        CharSequence choice = smartReplies.choices[replyIndex];
         b.setText(choice);
 
         OnDismissAction action = () -> {
-            smartReplyController.smartReplySent(entry, replyIndex, b.getText());
+            smartReplyController.smartReplySent(
+                    entry, replyIndex, b.getText(), smartReplies.fromAssistant);
             Bundle results = new Bundle();
-            results.putString(remoteInput.getResultKey(), choice.toString());
+            results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
             Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+            RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+                    results);
             RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
             entry.setHasSentReply();
             try {
-                pendingIntent.send(context, 0, intent);
+                smartReplies.pendingIntent.send(context, 0, intent);
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Unable to send smart reply", e);
             }
@@ -741,4 +743,40 @@
             return show;
         }
     }
+
+    /**
+     * Data class for smart replies.
+     */
+    public static class SmartReplies {
+        @NonNull
+        public final RemoteInput remoteInput;
+        @NonNull
+        public final PendingIntent pendingIntent;
+        @NonNull
+        public final CharSequence[] choices;
+        public final boolean fromAssistant;
+
+        public SmartReplies(CharSequence[] choices, RemoteInput remoteInput,
+                PendingIntent pendingIntent, boolean fromAssistant) {
+            this.choices = choices;
+            this.remoteInput = remoteInput;
+            this.pendingIntent = pendingIntent;
+            this.fromAssistant = fromAssistant;
+        }
+    }
+
+
+    /**
+     * Data class for smart actions.
+     */
+    public static class SmartActions {
+        @NonNull
+        public final List<Notification.Action> actions;
+        public final boolean fromAssistant;
+
+        public SmartActions(List<Notification.Action> actions, boolean fromAssistant) {
+            this.actions = actions;
+            this.fromAssistant = fromAssistant;
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index a26b1b5..0953951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,11 +19,9 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
 import android.content.ContentResolver;
@@ -34,10 +32,15 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.core.SliceQuery;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -50,13 +53,6 @@
 import java.util.HashSet;
 import java.util.concurrent.TimeUnit;
 
-import androidx.slice.Slice;
-import androidx.slice.SliceItem;
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.core.SliceQuery;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -164,7 +160,7 @@
         }
 
         @Override
-        protected boolean isDndSuppressingNotifications() {
+        protected boolean isDndOn() {
             return mIsZenMode;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 221cbe9..c28e74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -37,6 +37,7 @@
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
 
@@ -127,6 +128,8 @@
         resources.addOverride(R.integer.config_warningTemperature, 55);
 
         mPowerUI.start();
+        // Guarantees mHandler has processed all messages.
+        TestableLooper.get(this).processAllMessages();
         verify(mMockWarnings).showHighTemperatureWarning();
     }
 
@@ -139,6 +142,8 @@
         resources.addOverride(R.integer.config_warningTemperature, 55);
 
         mPowerUI.start();
+        // Guarantees mHandler has processed all messages.
+        TestableLooper.get(this).processAllMessages();
         verify(mMockWarnings).showHighTemperatureWarning();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 48491d7..24bcca50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -16,8 +16,12 @@
 
 package com.android.systemui.privacy
 
+import android.app.ActivityManager
 import android.app.AppOpsManager
+import android.content.Intent
 import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
 import android.support.test.filters.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -34,9 +38,11 @@
 import org.mockito.ArgumentMatchers.anyList
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
-
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
@@ -44,10 +50,18 @@
 @RunWithLooper
 class PrivacyItemControllerTest : SysuiTestCase() {
 
+    companion object {
+        val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+        val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+        const val TAG = "PrivacyItemControllerTest"
+    }
+
     @Mock
     private lateinit var appOpsController: AppOpsController
     @Mock
     private lateinit var callback: PrivacyItemController.Callback
+    @Mock
+    private lateinit var userManager: UserManager
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var privacyItemController: PrivacyItemController
@@ -57,15 +71,17 @@
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
 
-        appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java)
+        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
         mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+        mContext.addMockSystemService(UserManager::class.java, userManager)
 
         doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
                 .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
 
         privacyItemController = PrivacyItemController(mContext, callback)
     }
+
     @Test
     fun testSetListeningTrue() {
         privacyItemController.setListening(true)
@@ -80,6 +96,38 @@
         privacyItemController.setListening(true)
         privacyItemController.setListening(false)
         verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback:: class.java))
+                any(AppOpsController.Callback::class.java))
+    }
+
+    @Test
+    fun testRegisterReceiver_allUsers() {
+        val spiedContext = spy(mContext)
+        val itemController = PrivacyItemController(spiedContext, callback)
+
+        verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
+                eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
+                eq(null))
+        verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
+    }
+
+    @Test
+    fun testReceiver_ACTION_USER_FOREGROUND() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_USER_FOREGROUND))
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
+        verify(userManager).getProfiles(anyInt())
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f49c5b4..9d0556f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -120,7 +120,7 @@
         mTestHandler = Handler.createAsync(Looper.myLooper());
         mSbn = createNewNotification(0 /* id */);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mAlertingNotificationManager = createAlertingNotificationManager();
     }
@@ -171,7 +171,7 @@
         for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = createNewNotification(i);
             NotificationData.Entry entry = new NotificationData.Entry(sbn);
-            entry.row = mRow;
+            entry.setRow(mRow);
             mAlertingNotificationManager.showNotification(entry);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index d409e2b..b5d305d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -78,7 +78,7 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
                 mDelegate, mController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index aca1f90..9bed59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -227,7 +227,7 @@
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        entry.row = row;
+        entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
                 notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 602e613..f64e84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -37,6 +37,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -95,7 +96,7 @@
     private NotificationData.Entry createEntry() throws Exception {
         ExpandableNotificationRow row = mHelper.createRow();
         NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
-        entry.row = row;
+        entry.setRow(row);
         return entry;
     }
 
@@ -108,9 +109,9 @@
         NotificationData.Entry entry2 = createEntry();
 
         // Set up the prior state to look like three top level notifications.
-        mListContainer.addContainerView(entry0.row);
-        mListContainer.addContainerView(entry1.row);
-        mListContainer.addContainerView(entry2.row);
+        mListContainer.addContainerView(entry0.getRow());
+        mListContainer.addContainerView(entry1.getRow());
+        mListContainer.addContainerView(entry2.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
@@ -118,15 +119,15 @@
         when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
         when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
         when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
-        when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
-        when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+        when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
+        when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
 
         // Run updateNotifications - the view hierarchy should be reorganized.
         mViewHierarchyManager.updateNotificationViews();
 
-        verify(mListContainer).notifyGroupChildAdded(entry1.row);
-        verify(mListContainer).notifyGroupChildAdded(entry2.row);
-        assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+        verify(mListContainer).notifyGroupChildAdded(entry1.getRow());
+        verify(mListContainer).notifyGroupChildAdded(entry2.getRow());
+        assertTrue(Lists.newArrayList(entry0.getRow()).equals(mListContainer.mRows));
     }
 
     @Test
@@ -135,11 +136,11 @@
         NotificationData.Entry entry0 = createEntry();
         NotificationData.Entry entry1 = createEntry();
         NotificationData.Entry entry2 = createEntry();
-        entry0.row.addChildNotification(entry1.row);
-        entry0.row.addChildNotification(entry2.row);
+        entry0.getRow().addChildNotification(entry1.getRow());
+        entry0.getRow().addChildNotification(entry2.getRow());
 
         // Set up the prior state to look like one top level notification.
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
@@ -152,10 +153,12 @@
         mViewHierarchyManager.updateNotificationViews();
 
         verify(mListContainer).notifyGroupChildRemoved(
-                entry1.row, entry0.row.getChildrenContainer());
+                entry1.getRow(), entry0.getRow().getChildrenContainer());
         verify(mListContainer).notifyGroupChildRemoved(
-                entry2.row, entry0.row.getChildrenContainer());
-        assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+                entry2.getRow(), entry0.getRow().getChildrenContainer());
+        assertTrue(
+                Lists.newArrayList(entry0.getRow(), entry1.getRow(), entry2.getRow())
+                        .equals(mListContainer.mRows));
     }
 
     @Test
@@ -163,10 +166,10 @@
         // Tests two top level notifications becoming a suppressed summary and a child.
         NotificationData.Entry entry0 = createEntry();
         NotificationData.Entry entry1 = createEntry();
-        entry0.row.addChildNotification(entry1.row);
+        entry0.getRow().addChildNotification(entry1.getRow());
 
         // Set up the prior state to look like a top level notification.
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1));
 
@@ -179,23 +182,23 @@
         mViewHierarchyManager.updateNotificationViews();
 
         verify(mListContainer).notifyGroupChildRemoved(
-                entry1.row, entry0.row.getChildrenContainer());
-        assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
-        assertEquals(View.GONE, entry0.row.getVisibility());
-        assertEquals(View.VISIBLE, entry1.row.getVisibility());
+                entry1.getRow(), entry0.getRow().getChildrenContainer());
+        assertTrue(Lists.newArrayList(entry0.getRow(), entry1.getRow()).equals(mListContainer.mRows));
+        assertEquals(View.GONE, entry0.getRow().getVisibility());
+        assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
     }
 
     @Test
     public void testUpdateNotificationViews_appOps() throws Exception {
         NotificationData.Entry entry0 = createEntry();
-        entry0.row = spy(entry0.row);
+        entry0.setRow(spy(entry0.getRow()));
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
 
         mViewHierarchyManager.updateNotificationViews();
 
-        verify(entry0.row, times(1)).showAppOpsIcons(any());
+        verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
     }
 
     private class FakeListContainer implements NotificationListContainer {
@@ -252,7 +255,7 @@
         public void setMaxDisplayedNotifications(int maxNotifications) {}
 
         @Override
-        public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+        public void snapViewIfNeeded(Entry entry) {}
 
         @Override
         public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
@@ -271,10 +274,10 @@
         }
 
         @Override
-        public void cleanUpViewState(View view) {}
+        public void cleanUpViewStateForEntry(Entry entry) { }
 
         @Override
-        public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+        public boolean isInVisibleLocation(Entry entry) {
             return true;
         }
 
@@ -286,5 +289,6 @@
         public boolean hasPulsingNotifications() {
             return false;
         }
+
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 1d977d8..76b992f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -16,18 +16,15 @@
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -38,7 +35,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -93,7 +89,7 @@
 
     @Test
     public void testSendSmartReply_updatesRemoteInput() {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
 
         // Sending smart reply should make calls to NotificationEntryManager
         // to update the notification with reply and spinner.
@@ -103,11 +99,21 @@
 
     @Test
     public void testSendSmartReply_logsToStatusBar() throws RemoteException {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
 
         // Check we log the result to the status bar service.
         verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
-                TEST_CHOICE_INDEX);
+                TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
+    }
+
+
+    @Test
+    public void testSendSmartReply_logsToStatusBar_generatedByAssistant() throws RemoteException {
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
+
+        // Check we log the result to the status bar service.
+        verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
+                TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
     }
 
     @Test
@@ -121,14 +127,14 @@
 
     @Test
     public void testSendSmartReply_reportsSending() {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
 
         assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
     }
 
     @Test
     public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
         mSmartReplyController.stopSending(mEntry);
 
         assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index d1fe5af..8706e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -262,14 +262,14 @@
                 NotificationData.Entry.class);
         verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
         NotificationData.Entry entry = entryCaptor.getValue();
-        verify(mRemoteInputManager).bindRow(entry.row);
+        verify(mRemoteInputManager).bindRow(entry.getRow());
 
         // Row content inflation:
         verify(mCallback).onNotificationAdded(entry);
         verify(mPresenter).updateNotificationViews();
 
         assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
-        assertNotNull(entry.row);
+        assertNotNull(entry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
@@ -294,7 +294,7 @@
         verify(mPresenter).updateNotificationViews();
         verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
         verify(mCallback).onNotificationUpdated(mSbn);
-        assertNotNull(mEntry.row);
+        assertNotNull(mEntry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
     }
@@ -303,7 +303,7 @@
     public void testRemoveNotification() throws Exception {
         com.android.systemui.util.Assert.isNotMainThread();
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -313,7 +313,7 @@
 
         verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
         verify(mForegroundServiceController).removeNotification(mSbn);
-        verify(mListContainer).cleanUpViewState(mRow);
+        verify(mListContainer).cleanUpViewStateForEntry(mEntry);
         verify(mPresenter).updateNotificationViews();
         verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
         verify(mRow).setRemoved();
@@ -332,7 +332,7 @@
         extenders.clear();
         extenders.add(extender);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -347,7 +347,7 @@
 
         when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
                 .thenReturn(mEntry.key);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.updateNotificationsForAppOp(
@@ -372,7 +372,7 @@
 
     @Test
     public void testAddNotificationExistingAppOps() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         ArraySet<Integer> expected = new ArraySet<>();
         expected.add(3);
@@ -395,7 +395,7 @@
 
     @Test
     public void testAdd_noExistingAppOps() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         when(mForegroundServiceController.getStandardLayoutKey(
                 mEntry.notification.getUserId(),
@@ -409,7 +409,7 @@
 
     @Test
     public void testAdd_existingAppOpsNotForegroundNoti() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         ArraySet<Integer> ops = new ArraySet<>();
         ops.add(3);
@@ -431,7 +431,7 @@
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntry.setInflationTask(mAsyncInflationTask);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -447,7 +447,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, null);
 
@@ -461,7 +461,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = null;
+        mEntry.setRow(null);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
@@ -476,7 +476,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = null;
+        mEntry.setRow(null);
         mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index ffb1c2d..f190979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -47,7 +47,7 @@
     public void setUp() {
         mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
         mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 1c7a8e8..3710fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -89,7 +89,7 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mLogger = new TestableNotificationLogger(mBarService);
         mLogger.setUpWithContainer(mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index a6725b8..7fee0ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,11 +16,10 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.Mockito.doNothing;
@@ -106,7 +105,8 @@
         mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
         mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
 
-        // Smart replies
+        // Smart replies and actions
+        when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
         mEntry = new NotificationData.Entry(mStatusBarNotification);
         when(mSmartReplyConstants.isEnabled()).thenReturn(true);
@@ -169,8 +169,10 @@
     private void setupAppGeneratedReplies(
             CharSequence[] smartReplyTitles,
             Notification.Action freeFormRemoteInputAction) {
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
         Notification.Action action =
-                new Notification.Action.Builder(null, "Test Action", null).build();
+                new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
         when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
         Pair<RemoteInput, Notification.Action> remoteInputActionPair =
                 Pair.create(mRemoteInput, action);
@@ -190,7 +192,7 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertFalse(repliesAndActions.smartRepliesExist());
+        assertThat(repliesAndActions.smartReplies).isNull();
     }
 
     @Test
@@ -202,7 +204,9 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -218,8 +222,10 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
-        assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+        assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
+        assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
     }
 
     @Test
@@ -236,8 +242,9 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
-        assertThat(repliesAndActions.smartActions, is(empty()));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -254,8 +261,8 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(null));
-        assertThat(repliesAndActions.smartActions, is(empty()));
+        assertThat(repliesAndActions.smartReplies).isNull();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -269,8 +276,10 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(null));
-        assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+        assertThat(repliesAndActions.smartReplies).isNull();
+        assertThat(repliesAndActions.smartActions.actions)
+                .isEqualTo(mEntry.systemGeneratedSmartActions);
+        assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
     }
 
     @Test
@@ -295,8 +304,27 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
-        assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+        assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
+        assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // actions.
+        setupAppGeneratedReplies(null);
+
+        when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
+
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartActions).isNull();
+        assertThat(repliesAndActions.smartReplies).isNull();
     }
 
     private Notification.Action.Builder createActionBuilder(String actionTitle) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 3d2ea70..2797969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -54,6 +54,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
@@ -177,10 +178,17 @@
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
 
         ExpandableNotificationRow row = spy(realRow);
+
         when(row.getWindowToken()).thenReturn(new Binder());
         when(row.getGuts()).thenReturn(guts);
         doNothing().when(row).inflateGuts();
 
+        NotificationData.Entry realEntry = realRow.getEntry();
+        NotificationData.Entry entry = spy(realEntry);
+
+        when(entry.getRow()).thenReturn(row);
+        when(entry.getGuts()).thenReturn(guts);
+
         mGutsManager.openGuts(row, 0, 0, menuItem);
         mTestableLooper.processAllMessages();
         verify(guts).openControls(
@@ -190,13 +198,19 @@
                 anyBoolean(),
                 any(Runnable.class));
 
+        // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+        verify(row).setGutsView(any());
+
         row.onDensityOrFontScaleChanged();
-        mGutsManager.onDensityOrFontScaleChanged(row);
+        mGutsManager.onDensityOrFontScaleChanged(entry);
+
         mTestableLooper.processAllMessages();
 
         mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
 
         verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+
+        // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
         verify(row, times(2)).setGutsView(any());
     }
 
@@ -470,7 +484,7 @@
         ExpandableNotificationRow row = spy(createTestNotificationRow());
         doReturn(guts).when(row).getGuts();
         NotificationData.Entry entry = row.getEntry();
-        entry.row = row;
+        entry.setRow(row);
         mGutsManager.setExposedGuts(guts);
 
         assertTrue(mGutsManager.shouldExtendLifetime(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 1cc1c63..985827a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -134,7 +134,7 @@
                 .thenReturn(packageInfo);
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = TEST_UID;  // non-zero
-        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+        when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
                 applicationInfo);
         final PackageInfo systemPackageInfo = new PackageInfo();
         systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -206,6 +206,37 @@
     }
 
     @Test
+    public void testBindNotification_noDelegate() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+        final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+        assertEquals(GONE, nameView.getVisibility());
+        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+        assertEquals(GONE, dividerView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_delegate() throws Exception {
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = 7;  // non-zero
+        when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+                applicationInfo);
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+        final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+        assertEquals(VISIBLE, nameView.getVisibility());
+        assertTrue(nameView.getText().toString().contains("Other"));
+        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+        assertEquals(VISIBLE, dividerView.getVisibility());
+    }
+
+    @Test
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 74ce5f6..e65e806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -325,7 +325,9 @@
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        when(row.isClearable()).thenReturn(true);
+        NotificationData.Entry entry = mock(NotificationData.Entry.class);
+        when(row.getEntry()).thenReturn(entry);
+        when(entry.isClearable()).thenReturn(true);
         mStackScroller.addContainerView(row);
 
         mStackScroller.onUpdateRowStates();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 10b0d83..c99e766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -81,12 +81,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
     }
 
@@ -95,12 +95,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertTrue(mHeadsUpAppearanceController.isShown());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertFalse(mHeadsUpAppearanceController.isShown());
     }
 
@@ -109,12 +109,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
     }
 
@@ -125,12 +125,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1070795..44deb10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -97,7 +97,7 @@
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
         NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
-        laterEntry.row = mRow;
+        laterEntry.setRow(mRow);
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 96c57f2..c3bc511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -149,7 +149,8 @@
         Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         Entry childEntry = mGroupTestHelper.createChildNotification();
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -166,12 +167,14 @@
         Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         Entry childEntry = mGroupTestHelper.createChildNotification();
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(true);
         mGroupAlertTransferHelper.onInflationFinished(childEntry);
 
         // Alert is immediately removed from summary, and we show child as its content is inflated.
@@ -185,7 +188,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         NotificationData.Entry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
@@ -199,10 +203,12 @@
         mGroupManager.onEntryAdded(childEntry2);
 
         // Child entry finishes its inflation.
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(true);
         mGroupAlertTransferHelper.onInflationFinished(childEntry);
 
-        verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+        verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
+            .getContentFlag());
         assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
     }
 
@@ -212,7 +218,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -229,7 +236,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -251,7 +259,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 1483ae5..b0bd1fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -100,7 +100,7 @@
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
 
         assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
-        assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
     }
 
     @Test
@@ -143,9 +143,8 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
-        assertEquals(summaryEntry.row,
-                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
     }
 
     @Test
@@ -161,8 +160,7 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
-        assertEquals(summaryEntry.row,
-                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 01f44fd4..7ad68eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -81,7 +81,7 @@
                 0 /* postTime */);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        entry.row = row;
+        entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
         when(row.getStatusBarNotification()).thenReturn(sbn);
         when(row.isInflationFlagSet(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 9e659c8..b6e3fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -181,7 +181,16 @@
     public void testSendSmartReply_controllerCalled() {
         setSmartReplies(TEST_CHOICES);
         mView.getChildAt(2).performClick();
-        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
+        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+                false /* generatedByAsssitant */);
+    }
+
+    @Test
+    public void testSendSmartReply_controllerCalled_generatedByAssistant() {
+        setSmartReplies(TEST_CHOICES, true);
+        mView.getChildAt(2).performClick();
+        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+                true /* generatedByAsssitant */);
     }
 
     @Test
@@ -392,11 +401,17 @@
     }
 
     private void setSmartReplies(CharSequence[] choices) {
+        setSmartReplies(choices, false);
+    }
+
+    private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
+        SmartReplyView.SmartReplies smartReplies =
+                new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
         mView.resetSmartSuggestions(mContainer);
-        mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+        mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
     }
 
     private Notification.Action createAction(String actionTitle) {
@@ -415,12 +430,12 @@
 
     private void setSmartActions(String[] actionTitles) {
         mView.resetSmartSuggestions(mContainer);
-        mView.addSmartActions(createActions(actionTitles));
+        mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
     }
 
     private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
         setSmartReplies(choices);
-        mView.addSmartActions(createActions(actionTitles));
+        mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
     }
 
     private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -453,9 +468,11 @@
 
         // Add smart replies
         Button previous = null;
+        SmartReplyView.SmartReplies smartReplies =
+                new SmartReplyView.SmartReplies(choices, null, null, false);
         for (int i = 0; i < choices.length; ++i) {
-            Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
-                    null, null, null, null);
+            Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies,
+                    null, null);
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 17d8ea7..c56f31e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,11 +18,13 @@
 
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
+import static android.util.DebugUtils.flagsToString;
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sVerbose;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -72,9 +74,12 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.intelligence.IntelligenceManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -95,6 +100,27 @@
 
     private static final Object sLock = AutofillManagerService.class;
 
+
+    /**
+     * IME supports Smart Suggestions.
+     */
+    // NOTE: must be public because of flagsToString()
+    public static final int FLAG_SMART_SUGGESTION_IME = 0x1;
+
+    /**
+     * System supports Smarts Suggestions (as a popup-window similar to standard Autofill).
+     */
+    // NOTE: must be public because of flagsToString()
+    public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
+            FLAG_SMART_SUGGESTION_IME,
+            FLAG_SMART_SUGGESTION_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SmartSuggestionMode {}
+
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
     private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -102,7 +128,6 @@
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
 
-
     /**
      * Maximum number of partitions that can be allowed in a session.
      *
@@ -130,6 +155,7 @@
 
     private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
     private final LocalService mLocalService = new LocalService();
+    final IntelligenceManagerInternal mIntelligenceManagerInternal;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -153,13 +179,21 @@
     @GuardedBy("mLock")
     private boolean mAllowInstantService;
 
+    /**
+     * Supported modes for Augmented Autofill Smart Suggestions.
+     */
+    @GuardedBy("mLock")
+    private int mSupportedSmartSuggestionModes;
+
     public AutofillManagerService(Context context) {
         super(context, UserManager.DISALLOW_AUTOFILL);
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
+        mIntelligenceManagerInternal = LocalServices.getService(IntelligenceManagerInternal.class);
 
         setLogLevelFromSettings();
         setMaxPartitionsFromSettings();
         setMaxVisibleDatasetsFromSettings();
+        setSmartSuggestionEmulationFromSettings();
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -186,6 +220,9 @@
         resolver.registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
                 UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS), false, observer,
+                UserHandle.USER_ALL);
     }
 
     @Override // from AbstractMasterSystemService
@@ -200,6 +237,9 @@
             case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
                 setMaxVisibleDatasetsFromSettings();
                 break;
+            case Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS:
+                setSmartSuggestionEmulationFromSettings();
+                break;
             default:
                 Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
                 // fall through
@@ -243,6 +283,10 @@
         mUi.hideAll(null);
     }
 
+    @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+        return mSupportedSmartSuggestionModes;
+    }
+
     // Called by Shell command.
     void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
@@ -420,6 +464,19 @@
         }
     }
 
+    private void setSmartSuggestionEmulationFromSettings() {
+        final int flags = Settings.Global.getInt(getContext().getContentResolver(),
+                Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0);
+        if (sDebug) {
+            Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): "
+                    + smartSuggestionFlagsToString(flags));
+        }
+
+        synchronized (mLock) {
+            mSupportedSmartSuggestionModes = flags;
+        }
+    }
+
     // Called by Shell command.
     void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
@@ -610,6 +667,10 @@
         }
     }
 
+    static String smartSuggestionFlagsToString(int flags) {
+        return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags);
+    }
+
     private final class LocalService extends AutofillManagerInternal {
         @Override
         public void onBackKeyPressed() {
@@ -1158,6 +1219,10 @@
                     pw.print("from settings: ");
                     pw.println(getWhitelistedCompatModePackagesFromSettings());
                     pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+                    if (mSupportedSmartSuggestionModes != 0) {
+                        pw.print("Smart Suggestion modes: ");
+                        pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
+                    }
                     if (showHistory) {
                         pw.println(); pw.println("Requests history:"); pw.println();
                         mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 67ccc9b..0df99d4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -73,6 +73,7 @@
 import com.android.server.AbstractPerUserSystemService;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
 import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.PrintWriter;
@@ -268,8 +269,8 @@
         pruneAbandonedSessionsLocked();
 
         final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
-                appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed,
-                flags);
+                appCallbackToken, hasCallback, componentName, compatMode,
+                bindInstantServiceAllowed, flags);
         if (newSession == null) {
             return NO_SESSION;
         }
@@ -823,6 +824,12 @@
         return true;
     }
 
+    @GuardedBy("mLock")
+    @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+        // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master)
+        return mMaster.getSupportedSmartSuggestionModesLocked();
+    }
+
     @Override
     @GuardedBy("mLock")
     protected void dumpLocked(String prefix, PrintWriter pw) {
@@ -962,6 +969,9 @@
                 if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
                 session.forceRemoveSelfLocked();
             }
+            else {
+                session.destroyAugmentedAutofillWindowsLocked();
+            }
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1ff1acd..8676f7f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -25,6 +25,9 @@
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM;
+import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString;
 import static com.android.server.autofill.Helper.getNumericValue;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
@@ -93,8 +96,11 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.AbstractRemoteService;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.PendingUi;
+import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -242,6 +248,10 @@
     @GuardedBy("mLock")
     private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
 
+    @GuardedBy("mLock")
+    @Nullable
+    private AugmentedAutofillCallback mAugmentedAutofillCallback;
+
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -2497,15 +2507,83 @@
         processResponseLocked(newResponse, newClientState, 0);
     }
 
+    @GuardedBy("mLock")
     private void processNullResponseLocked(int flags) {
-        if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null");
         if ((flags & FLAG_MANUAL_REQUEST) != 0) {
             getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
         }
         mService.resetLastResponse();
-        // Nothing to be done, but need to notify client.
-        notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
-        removeSelf();
+
+        // The default autofill service cannot fullfill the request, let's check if the intelligence
+        // service can.
+        mAugmentedAutofillCallback = triggerAugmentedAutofillLocked();
+        if (mAugmentedAutofillCallback == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "canceling session " + id + " when server returned null and there is no"
+                        + " AugmentedAutofill for user");
+            }
+            // Nothing to be done, but need to notify client.
+            notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
+            removeSelf();
+        } else {
+            // TODO(b/111330312, b/119638958): must set internal state so when user focus other
+            // fields it does not generate a new call to the standard autofill service (right now
+            // it does). Must also add CTS tests to exercise this scenario.
+            if (sVerbose) {
+                Slog.v(TAG, "keeping session " + id + " when server returned null but "
+                        + "there is an AugmentedAutofill for user");
+            }
+        }
+    }
+
+    /**
+     * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
+     *
+     * @return callback to the Augmented Autofill service, or {@code null} if not supported.
+     */
+    // TODO(b/111330312): might need to call it in other places, like when the service returns a
+    // non-null response but without datasets (for example, just SaveInfo)
+    @GuardedBy("mLock")
+    private AugmentedAutofillCallback triggerAugmentedAutofillLocked() {
+        // Check if Smart Suggestions is supported...
+        final @SmartSuggestionMode int supportedModes = mService
+                .getSupportedSmartSuggestionModesLocked();
+        if (supportedModes == 0) return null;
+
+        // ...then if the service is set for the user
+        final IntelligenceManagerInternal intelligenceManagerInternal = mService
+                .getMaster().mIntelligenceManagerInternal;
+        if (intelligenceManagerInternal == null) return null;
+
+        // Define which mode will be used
+        final int mode;
+        if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) {
+            // TODO(b/111330312): support it :-)
+            Slog.w(TAG, "Smart Suggestions on IME not supported yet");
+            return null;
+        } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
+            mode = FLAG_SMART_SUGGESTION_SYSTEM;
+        } else {
+            Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes);
+            return null;
+        }
+
+        if (mCurrentViewId == null) {
+            Slog.w(TAG, "triggerAugmentedAutofillLocked(): no view currently focused");
+            return null;
+        }
+
+        if (sVerbose) {
+            Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId
+                    + " using suggestion mode " + smartSuggestionFlagsToString(mode)
+                    + " when server returned null for session " + this.id);
+        }
+
+        // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
+        // furgher AFM -> AFMS calls.
+        // TODO(b/119638958): add CTS tests
+        return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient,
+                mActivityToken, this.id, mCurrentViewId);
     }
 
     @GuardedBy("mLock")
@@ -2786,6 +2864,9 @@
         pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
                 mSaveOnAllViewsInvisible);
         pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+        if (mAugmentedAutofillCallback != null) {
+            pw.print(prefix); pw.println("has AugmentedAutofillCallback");
+        }
         mRemoteFillService.dump(prefix, pw);
     }
 
@@ -2957,6 +3038,14 @@
                 Slog.e(TAG, "Error notifying client to finish session", e);
             }
         }
+        destroyAugmentedAutofillWindowsLocked();
+    }
+
+    @GuardedBy("mLock")
+    void destroyAugmentedAutofillWindowsLocked() {
+        if (mAugmentedAutofillCallback != null) {
+            mAugmentedAutofillCallback.destroy();
+        }
     }
 
     /**
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index c9f7218..6af098b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -164,18 +164,15 @@
         }
 
         /**
-         * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+         * Don't write apks for system-bundled apps that are not upgraded.
          */
         private boolean shouldWriteApk(
                 ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
-            boolean isForwardLocked =
-                    (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
             boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
             boolean isUpdatedSystemApp =
                     (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
             return includeApks
                     && !isSharedStorage
-                    && !isForwardLocked
                     && (!isSystemApp || isUpdatedSystemApp);
         }
     }
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index b37888f..71d261c 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -163,6 +163,20 @@
     }
 
     /**
+     * Gets the user associated with this service.
+     */
+    public final @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * Gets the master service.
+     */
+    public final M getMaster() {
+        return mMaster;
+    }
+
+    /**
      * Gets this UID of the remote service this service binds to, or {@code -1} if the service is
      * disabled.
      */
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 181d7fd..73a34d6 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -205,6 +205,9 @@
 
     protected void scheduleUnbind() {
         cancelScheduledUnbind();
+        // TODO(b/111276913): implement "permanent binding"
+        // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
+        // it's not)
         mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
                 .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74c8023..564d35a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -894,10 +894,18 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
+        // Listen to package add and removal events for all users.
+        intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
         try {
             mNMS.registerObserver(mTethering);
             mNMS.registerObserver(mDataActivityObserver);
@@ -4155,6 +4163,7 @@
     }
 
     private void onUserAdded(int userId) {
+        mPermissionMonitor.onUserAdded(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -4165,6 +4174,7 @@
     }
 
     private void onUserRemoved(int userId) {
+        mPermissionMonitor.onUserRemoved(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -4174,6 +4184,22 @@
         }
     }
 
+    private void onPackageAdded(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageAdded(packageName, uid);
+    }
+
+    private void onPackageRemoved(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageRemoved(uid);
+    }
+
     private void onUserUnlocked(int userId) {
         synchronized (mVpns) {
             // User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -4185,11 +4211,15 @@
         }
     }
 
-    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
             if (userId == UserHandle.USER_NULL) return;
 
             if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -4202,6 +4232,10 @@
                 onUserRemoved(userId);
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 onUserUnlocked(userId);
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
             }
         }
     };
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 046442a..e5275e5 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -2990,7 +2991,7 @@
         ArrayList<Receiver> deadReceivers = null;
         ArrayList<UpdateRecord> deadUpdateRecords = null;
 
-        // Broadcast location or status to all listeners
+        // Broadcast location to all listeners
         for (UpdateRecord r : records) {
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
@@ -3049,14 +3050,19 @@
                 }
             }
 
-            long prevStatusUpdateTime = r.mLastStatusBroadcast;
-            if ((newStatusUpdateTime > prevStatusUpdateTime) &&
-                    (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+            // TODO: location provider status callbacks have been disabled and deprecated, and are
+            // guarded behind this setting now. should be removed completely post-Q
+            if (Settings.Global.getInt(mContext.getContentResolver(),
+                    LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
+                long prevStatusUpdateTime = r.mLastStatusBroadcast;
+                if ((newStatusUpdateTime > prevStatusUpdateTime)
+                        && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
 
-                r.mLastStatusBroadcast = newStatusUpdateTime;
-                if (!receiver.callStatusChangedLocked(provider, status, extras)) {
-                    receiverDead = true;
-                    Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+                    r.mLastStatusBroadcast = newStatusUpdateTime;
+                    if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+                        receiverDead = true;
+                        Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+                    }
                 }
             }
 
@@ -3276,7 +3282,6 @@
             // we don't leave anything dangling.
             clearTestProviderEnabled(provider, opPackageName);
             clearTestProviderLocation(provider, opPackageName);
-            clearTestProviderStatus(provider, opPackageName);
 
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
@@ -3409,21 +3414,6 @@
     }
 
     @Override
-    public void clearTestProviderStatus(String provider, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-            mockProvider.clearStatus();
-        }
-    }
-
-    @Override
     public PendingIntent createManageLocationPermissionIntent(String packageName,
             String permission) {
         Preconditions.checkNotNull(packageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e9e83c..5f9e349 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -497,6 +497,18 @@
     private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
 
     /**
+     * The number of binder proxies we need to have before we start warning and
+     * dumping debug info.
+     */
+    private static final int BINDER_PROXY_HIGH_WATERMARK = 6000;
+
+    /**
+     * Low watermark that needs to be met before we consider dumping info again,
+     * after already hitting the high watermark.
+     */
+    private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
+
+    /**
      * State indicating that there is no need for any blocking for network.
      */
     @VisibleForTesting
@@ -8477,7 +8489,8 @@
             mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
             mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
 
-            BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
+            BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
+                    BINDER_PROXY_LOW_WATERMARK);
             BinderInternal.nSetBinderProxyCountEnabled(true);
             BinderInternal.setBinderProxyCountCallback(
                     new BinderInternal.BinderProxyLimitListener() {
@@ -9217,11 +9230,6 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpBinderProxies(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
                 dumpLmkLocked(pw);
             }
             pw.println();
@@ -9235,6 +9243,19 @@
             }
             dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
         }
+        if (dumpPackage == null) {
+            // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
+            // outgoing binder calls to retrieve interface descriptors; while that is system code,
+            // there is nothing preventing an app from overriding this implementation by talking to
+            // the binder driver directly, and hang up system_server in the process. So, dump
+            // without locks held, and even then only when there is an unreasonably large number of
+            // proxies in the first place.
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
+        }
     }
 
     /**
@@ -9373,7 +9394,7 @@
                         cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
             } else if ("binder-proxies".equals(cmd)) {
                 if (opti >= args.length) {
-                    dumpBinderProxies(pw);
+                    dumpBinderProxies(pw, 0 /* minToDump */);
                 } else {
                     String uid = args[opti];
                     opti++;
@@ -9714,10 +9735,17 @@
         return false;
     }
 
-    void dumpBinderProxies(PrintWriter pw) {
+    void dumpBinderProxies(PrintWriter pw, int minCountToDumpInterfaces) {
         pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
-        dumpBinderProxyInterfaceCounts(pw,
-                "  Top proxy interface names held by SYSTEM");
+        final int proxyCount = BinderProxy.getProxyCount();
+        if (proxyCount >= minCountToDumpInterfaces) {
+            dumpBinderProxyInterfaceCounts(pw,
+                    "Top proxy interface names held by SYSTEM");
+        } else {
+            pw.print("Not dumping proxy interface counts because size ("
+                    + Integer.toString(proxyCount) + ") looks reasonable");
+            pw.println();
+        }
         dumpBinderProxiesCounts(pw,
                 "  Counts of Binder Proxies held by SYSTEM");
     }
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 4fe7d03..09df7e20 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -222,11 +222,11 @@
                         receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
                         receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
                         boolean result = requestAutofillData
-                                ? ActivityTaskManager.getService().requestAssistContextExtras(
+                                ? ActivityTaskManager.getService().requestAutofillData(this,
+                                        receiverExtras, topActivity, 0 /* flags */)
+                                : ActivityTaskManager.getService().requestAssistContextExtras(
                                         ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
-                                                /* focused= */ i == 0, /* newSessionId= */ i == 0)
-                                : ActivityTaskManager.getService().requestAutofillData(this,
-                                        receiverExtras, topActivity, 0 /* flags */);
+                                        /* focused= */ i == 0, /* newSessionId= */ i == 0);
                         if (result) {
                             mPendingDataCount++;
                         } else if (i == 0) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 94c94a5..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -27,10 +27,7 @@
 import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -42,7 +39,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -64,15 +60,14 @@
 public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
-    private static final Boolean SYSTEM = Boolean.TRUE;
-    private static final Boolean NETWORK = Boolean.FALSE;
+    protected static final Boolean SYSTEM = Boolean.TRUE;
+    protected static final Boolean NETWORK = Boolean.FALSE;
     private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     private final INetworkManagementService mNetd;
-    private final BroadcastReceiver mIntentReceiver;
 
     // Values are User IDs.
     private final Set<Integer> mUsers = new HashSet<>();
@@ -85,26 +80,6 @@
         mPackageManager = context.getPackageManager();
         mUserManager = UserManager.get(context);
         mNetd = netd;
-        mIntentReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
-                Uri appData = intent.getData();
-                String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
-                if (Intent.ACTION_USER_ADDED.equals(action)) {
-                    onUserAdded(user);
-                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    onUserRemoved(user);
-                } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    onAppAdded(appName, appUid);
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    onAppRemoved(appUid);
-                }
-            }
-        };
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -112,17 +87,6 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_ADDED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        intentFilter.addDataScheme("package");
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
         if (apps == null) {
             loge("No apps");
@@ -260,7 +224,14 @@
         }
     }
 
-    private synchronized void onUserAdded(int user) {
+    /**
+     * Called when a user is added. See {link #ACTION_USER_ADDED}.
+     *
+     * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserAdded(int user) {
         if (user < 0) {
             loge("Invalid user in onUserAdded: " + user);
             return;
@@ -272,7 +243,14 @@
         update(users, mApps, true);
     }
 
-    private synchronized void onUserRemoved(int user) {
+    /**
+     * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+     *
+     * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserRemoved(int user) {
         if (user < 0) {
             loge("Invalid user in onUserRemoved: " + user);
             return;
@@ -284,8 +262,8 @@
         update(users, mApps, false);
     }
 
-
-    private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+    @VisibleForTesting
+    protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
         if (currentPermission == SYSTEM) {
             return currentPermission;
         }
@@ -303,33 +281,39 @@
         return currentPermission;
     }
 
-    private synchronized void onAppAdded(String appName, int appUid) {
-        if (TextUtils.isEmpty(appName) || appUid < 0) {
-            loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
-            return;
-        }
-
+    /**
+     * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+     *
+     * @param packageName The name of the new package.
+     * @param uid The uid of the new package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageAdded(String packageName, int uid) {
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
-        final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
-        if (permission != mApps.get(appUid)) {
-            mApps.put(appUid, permission);
+        final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+        if (permission != mApps.get(uid)) {
+            mApps.put(uid, permission);
 
             Map<Integer, Boolean> apps = new HashMap<>();
-            apps.put(appUid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         }
     }
 
-    private synchronized void onAppRemoved(int appUid) {
-        if (appUid < 0) {
-            loge("Invalid app in onAppRemoved: " + appUid);
-            return;
-        }
+    /**
+     * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+     *
+     * @param uid containing the integer uid previously assigned to the package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageRemoved(int uid) {
         Map<Integer, Boolean> apps = new HashMap<>();
 
         Boolean permission = null;
-        String[] packages = mPackageManager.getPackagesForUid(appUid);
+        String[] packages = mPackageManager.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
             for (String name : packages) {
                 permission = highestPermissionForUid(permission, name);
@@ -341,16 +325,16 @@
                 }
             }
         }
-        if (permission == mApps.get(appUid)) {
+        if (permission == mApps.get(uid)) {
             // The permissions of this UID have not changed. Nothing to do.
             return;
         } else if (permission != null) {
-            mApps.put(appUid, permission);
-            apps.put(appUid, permission);
+            mApps.put(uid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         } else {
-            mApps.remove(appUid);
-            apps.put(appUid, NETWORK);  // doesn't matter which permission we pick here
+            mApps.remove(uid);
+            apps.put(uid, NETWORK);  // doesn't matter which permission we pick here
             update(mUsers, apps, false);
         }
     }
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index aac83b6..6fe6324 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -19,6 +19,8 @@
 import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 
 /**
  * Intelligence Manager local system service interface.
@@ -41,4 +43,37 @@
      */
     public abstract boolean sendActivityAssistData(@UserIdInt int userId,
             @NonNull IBinder activityToken, @NonNull Bundle data);
+
+    /**
+     * Asks the intelligence service to provide Augmented Autofill for a given activity.
+     *
+     * @param userId user handle
+     * @param client binder used to communicate with the activity that originated this request.
+     * @param activityToken activity that originated this request.
+     * @param autofillSessionId autofill session id (must be used on {@code client} calls.
+     * @param focusedId id of the the field that triggered this request.
+     *
+     * @return {@code false} if the service cannot handle this request, {@code true} otherwise.
+     * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the
+     * service is disabled (or the activity blacklisted).
+     */
+    public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+            @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+            int autofillSessionId, @NonNull AutofillId focusedId);
+
+    /**
+     * Callback used by the Autofill Session to communicate with the Augmented Autofill service.
+     */
+    public interface AugmentedAutofillCallback {
+        // TODO(b/111330312): this method is calling when the Autofill session is destroyed, the
+        // main reason being the cases where user tap HOME.
+        // Right now it's completely destroying the UI, but we need to decide whether / how to
+        // properly recover it later (for example, if the user switches back to the activity,
+        // should it be restored? Right not it kind of is, because Autofill's Session trigger a
+        // new FillRequest, which in turn triggers the Augmented Autofill request again)
+        /**
+         * Destroys the Autofill UI.
+         */
+        void destroy();
+    }
 }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9e6e381..d5e4681 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -97,8 +97,8 @@
  *
  * {@hide}
  */
-public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback,
-        GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends LocationProviderInterface
+        implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
 
     private static final String TAG = "GnssLocationProvider";
 
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..6785964 100644
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/core/java/com/android/server/location/LocationProviderInterface.java
@@ -16,33 +16,63 @@
 
 package com.android.server.location;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
 
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 
-
-import android.os.Bundle;
-import android.os.WorkSource;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * Location Manager's interface for location providers.
  * @hide
  */
-public interface LocationProviderInterface {
-    public String getName();
+public abstract class LocationProviderInterface {
 
-    public void enable();
-    public void disable();
-    public boolean isEnabled();
-    public void setRequest(ProviderRequest request, WorkSource source);
+    /** Get name. */
+    public abstract String getName();
 
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+    /** Enable. */
+    public abstract void enable();
 
-    // --- deprecated (but still supported) ---
-    public ProviderProperties getProperties();
-    public int getStatus(Bundle extras);
-    public long getStatusUpdateTime();
-    public boolean sendExtraCommand(String command, Bundle extras);
+    /** Disable. */
+    public abstract void disable();
+
+    /** Is enabled. */
+    public abstract boolean isEnabled();
+
+    /** Set request. */
+    public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+    /** dump. */
+    public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+    /** Get properties. */
+    public abstract ProviderProperties getProperties();
+
+    /**
+     * Get status.
+     *
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
+    public int getStatus(Bundle extras) {
+        return LocationProvider.AVAILABLE;
+    }
+
+    /**
+     * Get status update time.
+     *
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
+    public long getStatusUpdateTime() {
+        return 0;
+    }
+
+    /** Send extra command. */
+    public abstract boolean sendExtraCommand(String command, Bundle extras);
 }
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index bb86b48..b408414 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -41,7 +41,7 @@
 /**
  * Proxy for ILocationProvider implementations.
  */
-public class LocationProviderProxy implements LocationProviderInterface {
+public class LocationProviderProxy extends LocationProviderInterface {
     private static final String TAG = "LocationProviderProxy";
     private static final boolean D = LocationManagerService.D;
 
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 8578761..145aee3 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -25,31 +25,31 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
 /**
  * A mock location provider used by LocationManagerService to implement test providers.
  *
  * {@hide}
  */
-public class MockProvider implements LocationProviderInterface {
+public class MockProvider extends LocationProviderInterface {
     private final String mName;
     private final ProviderProperties mProperties;
     private final ILocationManager mLocationManager;
 
     private final Location mLocation;
-    private final Bundle mExtras = new Bundle();
+
+    private boolean mHasLocation;
+    private boolean mEnabled;
+
 
     private int mStatus;
     private long mStatusUpdateTime;
-    private boolean mHasLocation;
-    private boolean mHasStatus;
-    private boolean mEnabled;
+    private Bundle mExtras;
 
     private static final String TAG = "MockProvider";
 
@@ -61,6 +61,10 @@
         mLocationManager = locationManager;
         mProperties = properties;
         mLocation = new Location(name);
+
+        mStatus = LocationProvider.AVAILABLE;
+        mStatusUpdateTime = 0L;
+        mExtras = null;
     }
 
     @Override
@@ -90,13 +94,12 @@
 
     @Override
     public int getStatus(Bundle extras) {
-        if (mHasStatus) {
+        if (mExtras != null) {
             extras.clear();
             extras.putAll(mExtras);
-            return mStatus;
-        } else {
-            return LocationProvider.AVAILABLE;
         }
+
+        return mStatus;
     }
 
     @Override
@@ -120,19 +123,14 @@
         mHasLocation = false;
     }
 
+    /**
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
     public void setStatus(int status, Bundle extras, long updateTime) {
         mStatus = status;
         mStatusUpdateTime = updateTime;
-        mExtras.clear();
-        if (extras != null) {
-            mExtras.putAll(extras);
-        }
-        mHasStatus = true;
-    }
-
-    public void clearStatus() {
-        mHasStatus = false;
-        mStatusUpdateTime = 0;
+        mExtras = extras;
     }
 
     @Override
@@ -145,9 +143,6 @@
         pw.println(prefix + "mHasLocation=" + mHasLocation);
         pw.println(prefix + "mLocation:");
         mLocation.dump(new PrintWriterPrinter(pw), prefix + "  ");
-        pw.println(prefix + "mHasStatus=" + mHasStatus);
-        pw.println(prefix + "mStatus=" + mStatus);
-        pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
         pw.println(prefix + "mExtras=" + mExtras);
     }
 
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 71bae07..99c9214 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -16,22 +16,20 @@
 
 package com.android.server.location;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
 import android.location.Criteria;
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
-import android.location.LocationProvider;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
 
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * A passive location provider reports locations received from other providers
@@ -40,7 +38,7 @@
  *
  * {@hide}
  */
-public class PassiveProvider implements LocationProviderInterface {
+public class PassiveProvider extends LocationProviderInterface {
     private static final String TAG = "PassiveProvider";
 
     private static final ProviderProperties PROPERTIES = new ProviderProperties(
@@ -78,20 +76,6 @@
     }
 
     @Override
-    public int getStatus(Bundle extras) {
-        if (mReportLocation) {
-            return LocationProvider.AVAILABLE;
-        } else {
-            return LocationProvider.TEMPORARILY_UNAVAILABLE;
-        }
-    }
-
-    @Override
-    public long getStatusUpdateTime() {
-        return -1;
-    }
-
-    @Override
     public void setRequest(ProviderRequest request, WorkSource source) {
         mReportLocation = request.reportLocation;
     }
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index decdac6..9222740 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -45,5 +45,15 @@
     void onNotificationDirectReplied(String key);
     void onNotificationSettingsViewed(String key);
     void onNotificationSmartRepliesAdded(String key, int replyCount);
-    void onNotificationSmartReplySent(String key, int replyIndex);
+
+    /**
+     * Notifies a smart reply is sent.
+     *
+     * @param key the notification key
+     * @param clickedIndex the index of clicked reply
+     * @param reply the reply that is sent
+     * @param generatedByAssistant specifies is the reply generated by NAS
+     */
+    void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
+            boolean generatedByAssistant);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da29e4..6005872 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -246,6 +246,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 /** {@hide} */
@@ -887,6 +888,7 @@
                     EventLogTags.writeNotificationExpansion(key,
                             userAction ? 1 : 0, expanded ? 1 : 0,
                             r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+                    mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
                 }
             }
         }
@@ -902,6 +904,7 @@
                             .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
                             .setType(MetricsEvent.TYPE_ACTION));
                     reportUserInteraction(r);
+                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
                 }
             }
         }
@@ -917,7 +920,8 @@
         }
 
         @Override
-        public void onNotificationSmartReplySent(String key, int replyIndex) {
+        public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
+                boolean generatedByAssistant) {
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
@@ -927,6 +931,8 @@
                     mMetricsLogger.write(logMaker);
                     // Treat clicking on a smart reply as a user interaction.
                     reportUserInteraction(r);
+                    mAssistants.notifyAssistantSuggestedReplySent(
+                            r.sbn, reply, generatedByAssistant);
                 }
             }
         }
@@ -4398,19 +4404,20 @@
      *
      * Has side effects.
      */
-    private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
+    private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
             NotificationRecord r, boolean isAutogroup) {
         final String pkg = r.sbn.getPackageName();
         final boolean isSystemNotification =
-                isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
+                isUidSystemOrPhone(uid) || ("android".equals(pkg));
         final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
 
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
             synchronized (mNotificationLock) {
+                final int callingUid = Binder.getCallingUid();
                 if (mNotificationsByKey.get(r.sbn.getKey()) == null
-                        && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
+                        && isCallerInstantApp(callingUid, userId)) {
                     // Ephemeral apps have some special constraints for notifications.
                     // They are not allowed to create new notifications however they are allowed to
                     // update notifications created by the system (e.g. a foreground service
@@ -4729,7 +4736,7 @@
                 mRankingHelper.extractSignals(r);
                 // tell the assistant service about the notification
                 if (mAssistants.isEnabled()) {
-                    mAssistants.onNotificationEnqueued(r);
+                    mAssistants.onNotificationEnqueuedLocked(r);
                     mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                             DELAY_FOR_ASSISTANT_TIME);
                 } else {
@@ -6521,24 +6528,28 @@
     }
 
     @VisibleForTesting
-    boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
+    boolean isCallerInstantApp(int callingUid, int userId) {
         // System is always allowed to act for ephemeral apps.
         if (isUidSystemOrPhone(callingUid)) {
             return false;
         }
 
-        mAppOps.checkPackage(callingUid, pkg);
-
         try {
+            final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+            if (pkgs == null) {
+                throw new SecurityException("Unknown uid " + callingUid);
+            }
+            final String pkg = pkgs[0];
+            mAppOps.checkPackage(callingUid, pkg);
+
             ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
             if (ai == null) {
                 throw new SecurityException("Unknown package " + pkg);
             }
             return ai.isInstantApp();
         } catch (RemoteException re) {
-            throw new SecurityException("Unknown package " + pkg, re);
+            throw new SecurityException("Unknown uid " + callingUid, re);
         }
-
     }
 
     private void checkCallerIsSameApp(String pkg) {
@@ -6842,69 +6853,125 @@
             }
         }
 
-        public void onNotificationEnqueued(final NotificationRecord r) {
+        @GuardedBy("mNotificationLock")
+        private void onNotificationEnqueuedLocked(final NotificationRecord r) {
             final StatusBarNotification sbn = r.sbn;
-            TrimCache trimCache = new TrimCache(sbn);
-
-            // There should be only one, but it's a list, so while we enforce
-            // singularity elsewhere, we keep it general here, to avoid surprises.
-            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info)
-                        && info.isSameUser(r.getUserId());
-                if (!sbnVisible) {
-                    continue;
-                }
-
-                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyEnqueued(info, sbnToPost, r.getChannel());
-                    }
-                });
-            }
+            notifyAssistantLocked(
+                    sbn,
+                    true /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+                        }
+                    });
         }
 
-        private void notifyEnqueued(final ManagedServiceInfo info,
-                final StatusBarNotification sbn, final NotificationChannel channel) {
-            final INotificationListener assistant = (INotificationListener) info.service;
-            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
-            try {
-                assistant.onNotificationEnqueuedWithChannel(sbnHolder, channel);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
-            }
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantExpansionChangedLocked(
+                final StatusBarNotification sbn,
+                final boolean isUserAction,
+                final boolean isExpanded) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+                        }
+                    });
         }
 
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantNotificationDirectReplyLocked(
+                final StatusBarNotification sbn) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationDirectReply(key);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+                        }
+                    });
+        }
+
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantSuggestedReplySent(
+                final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onSuggestedReplySent(
+                                    key,
+                                    reply,
+                                    generatedByAssistant
+                                            ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+                                            : NotificationAssistantService.SOURCE_FROM_APP);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+                        }
+                    });
+        }
+
+
         /**
          * asynchronously notify the assistant that a notification has been snoozed until a
          * context
          */
         @GuardedBy("mNotificationLock")
-        public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
-                final String snoozeCriterionId) {
-            TrimCache trimCache = new TrimCache(sbn);
-            for (final ManagedServiceInfo info : getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info);
-                if (!sbnVisible) {
-                    continue;
-                }
-                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        final INotificationListener assistant =
-                                (INotificationListener) info.service;
-                        StatusBarNotificationHolder sbnHolder
-                                = new StatusBarNotificationHolder(sbnToPost);
+        private void notifyAssistantSnoozedLocked(
+                final StatusBarNotification sbn, final String snoozeCriterionId) {
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
                         try {
                             assistant.onNotificationSnoozedUntilContext(
                                     sbnHolder, snoozeCriterionId);
                         } catch (RemoteException ex) {
                             Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
                         }
-                    }
-                });
+                    });
+        }
+
+        /**
+         * Notifies the assistant something about the specified notification, only assistant
+         * that is visible to the notification will be notified.
+         *
+         * @param sbn          the notification object that the update is about.
+         * @param sameUserOnly should the update  be sent to the assistant in the same user only.
+         * @param callback     the callback that provides the assistant to be notified, executed
+         *                     in WorkerHandler.
+         */
+        @GuardedBy("mNotificationLock")
+        private void notifyAssistantLocked(
+                final StatusBarNotification sbn,
+                boolean sameUserOnly,
+                BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+            TrimCache trimCache = new TrimCache(sbn);
+            // There should be only one, but it's a list, so while we enforce
+            // singularity elsewhere, we keep it general here, to avoid surprises.
+            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+                boolean sbnVisible = isVisibleToListener(sbn, info)
+                        && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
+                if (!sbnVisible) {
+                    continue;
+                }
+                final INotificationListener assistant = (INotificationListener) info.service;
+                final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+                final StatusBarNotificationHolder sbnHolder =
+                        new StatusBarNotificationHolder(sbnToPost);
+                mHandler.post(() -> callback.accept(assistant, sbnHolder));
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cc640f0..093b85e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,6 +16,28 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
+import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -41,6 +63,8 @@
 import com.android.server.pm.dex.DexoptUtils;
 import com.android.server.pm.dex.PackageDexUsage;
 
+import dalvik.system.DexFile;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -48,32 +72,6 @@
 import java.util.List;
 import java.util.Map;
 
-import dalvik.system.DexFile;
-
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
-
-import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
-import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
-import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
-import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
-import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
-import static com.android.server.pm.Installer.DEXOPT_FORCE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
-import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-
-import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
-
-import static dalvik.system.DexFile.getSafeModeCompilerFilter;
-import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
-
 /**
  * Helper class for running dexopt command on packages.
  */
@@ -544,8 +542,7 @@
         // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
         // the user does not have an existing profile.
         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
-        boolean isPublic = !info.isForwardLocked() &&
-                (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
+        boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
         // Some apps are executed with restrictions on hidden API usage. If this app is one
         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1a5b86c..2e9a71a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -455,12 +455,6 @@
                         + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
             }
 
-            if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
-                    || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                throw new IllegalArgumentException(
-                        "New installs into ASEC containers no longer supported");
-            }
-
             // Defensively resize giant app icons
             if (params.appIcon != null) {
                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -487,21 +481,10 @@
                 if (!PackageHelper.fitsOnInternal(mContext, params)) {
                     throw new IOException("No suitable internal storage available");
                 }
-
-            } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                if (!PackageHelper.fitsOnExternal(mContext, params)) {
-                    throw new IOException("No suitable external storage available");
-                }
-
-            } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
-                // For now, installs to adopted media are treated as internal from
-                // an install flag point-of-view.
-                params.setInstallFlagsInternal();
-
             } else {
                 // For now, installs to adopted media are treated as internal from
                 // an install flag point-of-view.
-                params.setInstallFlagsInternal();
+                params.installFlags |= PackageManager.INSTALL_INTERNAL;
 
                 // Resolve best location for install, based on combination of
                 // requested install flags, delta size, and manifest settings.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index acbd81d..6038e24 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,7 +43,6 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -646,9 +645,6 @@
     /** Directory where installed application's 32-bit native libraries are copied. */
     private static final File sAppLib32InstallDir =
             new File(Environment.getDataDirectory(), "app-lib");
-    /** Directory where code and non-resource assets of forward-locked applications are stored */
-    private static final File sDrmAppPrivateInstallDir =
-            new File(Environment.getDataDirectory(), "app-private");
 
     // ----------------------------------------------------------------
 
@@ -1811,12 +1807,10 @@
                             firstUserIds, firstInstantUserIds);
                 }
 
-                // Send broadcast package appeared if forward locked/external for all users
-                // treat asec-hosted packages like removable media on upgrade
-                if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+                // Send broadcast package appeared if external for all users
+                if (isExternal(res.pkg)) {
                     if (DEBUG_INSTALL) {
-                        Slog.i(TAG, "upgrading pkg " + res.pkg
-                                + " is ASEC-hosted -> AVAILABLE");
+                        Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
                     }
                     final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
                     ArrayList<String> pkgList = new ArrayList<>(1);
@@ -2629,10 +2623,6 @@
                         SystemClock.uptimeMillis());
                 scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
-                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
-                        | PackageParser.PARSE_FORWARD_LOCK,
-                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
                 // Remove disable package settings for updated system apps that were
                 // removed via an OTA. If the update is no longer present, remove the
                 // app completely. Otherwise, revoke their system privileges.
@@ -8631,7 +8621,7 @@
                     + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
 
             final InstallArgs args = createInstallArgsForExisting(
-                    packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+                    pkgSetting.codePathString,
                     pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
             args.cleanUpResourcesLI();
             synchronized (mPackages) {
@@ -8704,7 +8694,7 @@
                         + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
                         + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
                 InstallArgs args = createInstallArgsForExisting(
-                        packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+                        pkgSetting.codePathString,
                         pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -11536,11 +11526,8 @@
         // pass once we've determined ABI below.
         setNativeLibraryPaths(pkg, sAppLib32InstallDir);
 
-        // We would never need to extract libs for forward-locked and external packages,
-        // since the container service will do it for us. We shouldn't attempt to
-        // extract libs from system app when it was not updated.
-        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
-                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
+        // We shouldn't attempt to extract libs from system app when it was not updated.
+        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
             extractLibs = false;
         }
 
@@ -11881,7 +11868,6 @@
         final String codePath = pkg.codePath;
         final File codeFile = new File(codePath);
         final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-        final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
 
         info.nativeLibraryRootDir = null;
         info.nativeLibraryRootRequiresIsa = false;
@@ -11910,9 +11896,6 @@
                     info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
                             secondaryLibDir, apkName).getAbsolutePath();
                 }
-            } else if (asecApp) {
-                info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
-                        .getAbsolutePath();
             } else {
                 final String apkName = deriveCodePathName(codePath);
                 info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
@@ -14021,7 +14004,6 @@
         private int installLocationPolicy(PackageInfoLite pkgLite) {
             String packageName = pkgLite.packageName;
             int installLocation = pkgLite.installLocation;
-            boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             // reader
             synchronized (mPackages) {
                 // Currently installed package which the new package is attempting to replace or
@@ -14074,16 +14056,8 @@
                     if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                         // Check for updated system application.
                         if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                            if (onSd) {
-                                Slog.w(TAG, "Cannot install update to system app on sdcard");
-                                return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
-                            }
                             return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                         } else {
-                            if (onSd) {
-                                // Install flag overrides everything.
-                                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
-                            }
                             // If current upgrade specifies particular preference
                             if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
                                 // Application explicitly specified internal.
@@ -14104,11 +14078,6 @@
                     }
                 }
             }
-            // All the special cases have been taken care of.
-            // Return result based on recommended install location.
-            if (onSd) {
-                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
-            }
             return pkgLite.recommendedInstallLocation;
         }
 
@@ -14125,70 +14094,59 @@
             if (origin.staged) {
                 if (origin.file != null) {
                     installFlags |= PackageManager.INSTALL_INTERNAL;
-                    installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                 } else {
                     throw new IllegalStateException("Invalid stage location");
                 }
             }
 
-            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
             final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
             PackageInfoLite pkgLite = null;
 
-            if (onInt && onSd) {
-                // Check if both bits are set.
-                Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
-                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            } else if (onSd && ephemeral) {
-                Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
-                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            } else {
-                pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
-                        origin.resolvedPath, installFlags, packageAbiOverride);
 
-                if (DEBUG_INSTANT && ephemeral) {
-                    Slog.v(TAG, "pkgLite for install: " + pkgLite);
+            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+                    origin.resolvedPath, installFlags, packageAbiOverride);
+
+            if (DEBUG_INSTANT && ephemeral) {
+                Slog.v(TAG, "pkgLite for install: " + pkgLite);
+            }
+
+            /*
+             * If we have too little free space, try to free cache
+             * before giving up.
+             */
+            if (!origin.staged && pkgLite.recommendedInstallLocation
+                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+                // TODO: focus freeing disk space on the target device
+                final StorageManager storage = StorageManager.from(mContext);
+                final long lowThreshold = storage.getStorageLowBytes(
+                        Environment.getDataDirectory());
+
+                final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+                        origin.resolvedPath, packageAbiOverride);
+                if (sizeBytes >= 0) {
+                    try {
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+                                origin.resolvedPath, installFlags, packageAbiOverride);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to free cache", e);
+                    }
                 }
 
                 /*
-                 * If we have too little free space, try to free cache
-                 * before giving up.
+                 * The cache free must have deleted the file we downloaded to install.
+                 *
+                 * TODO: fix the "freeCache" call to not delete the file we care about.
                  */
-                if (!origin.staged && pkgLite.recommendedInstallLocation
-                        == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
-                    // TODO: focus freeing disk space on the target device
-                    final StorageManager storage = StorageManager.from(mContext);
-                    final long lowThreshold = storage.getStorageLowBytes(
-                            Environment.getDataDirectory());
-
-                    final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
-                            origin.resolvedPath, packageAbiOverride);
-                    if (sizeBytes >= 0) {
-                        try {
-                            mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
-                            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
-                                    origin.resolvedPath, installFlags, packageAbiOverride);
-                        } catch (InstallerException e) {
-                            Slog.w(TAG, "Failed to free cache", e);
-                        }
-                    }
-
-                    /*
-                     * The cache free must have deleted the file we
-                     * downloaded to install.
-                     *
-                     * TODO: fix the "freeCache" call to not delete
-                     *       the file we care about.
-                     */
-                    if (pkgLite.recommendedInstallLocation
-                            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
-                        pkgLite.recommendedInstallLocation
+                if (pkgLite.recommendedInstallLocation
+                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+                    pkgLite.recommendedInstallLocation
                             = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
-                    }
                 }
             }
 
+
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 int loc = pkgLite.recommendedInstallLocation;
                 if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
@@ -14208,24 +14166,21 @@
                     loc = installLocationPolicy(pkgLite);
                     if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                         ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-                    } else if (!onSd && !onInt) {
+                    } else if (!onInt) {
                         // Override install location with flags
                         if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                             // Set the flag to install on external media.
-                            installFlags |= PackageManager.INSTALL_EXTERNAL;
                             installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
                             if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                             }
                             installFlags |= PackageManager.INSTALL_INSTANT_APP;
-                            installFlags &= ~(PackageManager.INSTALL_EXTERNAL
-                                    |PackageManager.INSTALL_INTERNAL);
+                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else {
                             // Make sure the flag for installing on external
                             // media is unset
                             installFlags |= PackageManager.INSTALL_INTERNAL;
-                            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                         }
                     }
                 }
@@ -14409,7 +14364,7 @@
      * Create args that describe an existing installed package. Typically used
      * when cleaning up old installs, or used as a move source.
      */
-    private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
+    private InstallArgs createInstallArgsForExisting(String codePath,
             String resourcePath, String[] instructionSets) {
         return new FileInstallArgs(codePath, resourcePath, instructionSets);
     }
@@ -14501,14 +14456,6 @@
             return PackageManager.INSTALL_SUCCEEDED;
         }
 
-        protected boolean isFwdLocked() {
-            return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
-        }
-
-        protected boolean isExternalAsec() {
-            return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
-        }
-
         protected boolean isEphemeral() {
             return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
         }
@@ -14536,7 +14483,7 @@
     }
 
     /**
-     * Logic to handle installation of non-ASEC applications, including copying
+     * Logic to handle installation of new applications, including copying
      * and renaming logic.
      */
     class FileInstallArgs extends InstallArgs {
@@ -14558,9 +14505,6 @@
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie, params.signingDetails,
                     params.installReason, params.mParentInstallParams);
-            if (isFwdLocked()) {
-                throw new IllegalArgumentException("Forward locking only supported in ASEC");
-            }
         }
 
         /** Existing install */
@@ -15291,7 +15235,7 @@
                         // We didn't need to disable the .apk as a current system package,
                         // which means we are replacing another update that is already
                         // installed.  We need to make sure to delete the older one's .apk.
-                        res.removedInfo.args = createInstallArgsForExisting(0,
+                        res.removedInfo.args = createInstallArgsForExisting(
                                 oldPackage.applicationInfo.getCodePath(),
                                 oldPackage.applicationInfo.getResourcePath(),
                                 getAppDexInstructionSets(oldPackage.applicationInfo));
@@ -15570,8 +15514,6 @@
      */
     private void executePostCommitSteps(CommitRequest commitRequest) {
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
-            final boolean forwardLocked =
-                    ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
             final boolean instantApp =
                     ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
             final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
@@ -15604,10 +15546,8 @@
             //     This update happens in place!
             //
             // We only need to dexopt if the package meets ALL of the following conditions:
-            //   1) it is not forward locked.
-            //   2) it is not on on an external ASEC container.
-            //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
-            //   4) it is not debuggable.
+            //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
+            //   2) it is not debuggable.
             //
             // Note that we do not dexopt instant apps by default. dexopt can take some time to
             // complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15615,9 +15555,8 @@
             // continuous progress to the useur instead of mysteriously blocking somewhere in the
             // middle of running an instant app. The default behaviour can be overridden
             // via gservices.
-            final boolean performDexopt = !forwardLocked
-                    && !pkg.applicationInfo.isExternalAsec()
-                    && (!instantApp || Global.getInt(mContext.getContentResolver(),
+            final boolean performDexopt =
+                    (!instantApp || Global.getInt(mContext.getContentResolver(),
                     Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                     && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
 
@@ -15712,7 +15651,6 @@
                     // Parse old package
                     boolean oldExternal = isExternal(oldPackage);
                     int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
-                            | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
                             | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
                     int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
                     try {
@@ -15830,9 +15768,7 @@
         final String installerPackageName = args.installerPackageName;
         final String volumeUuid = args.volumeUuid;
         final File tmpPackageFile = new File(args.getCodePath());
-        final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
-        final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
-                || (args.volumeUuid != null));
+        final boolean onExternal = args.volumeUuid != null;
         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
         final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
         final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
@@ -15859,16 +15795,14 @@
         if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
 
         // Sanity check
-        if (instantApp && (forwardLocked || onExternal)) {
-            Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
-                    + " external=" + onExternal);
+        if (instantApp && onExternal) {
+            Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
             throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
         }
 
         // Retrieve PackageSettings and parse package
         @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | PackageParser.PARSE_ENFORCE_CODE
-                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
                 | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
 
@@ -16213,7 +16147,7 @@
                 pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
             }
 
-        } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+        } else {
             // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
             scanFlags |= SCAN_NO_DEX;
 
@@ -16745,19 +16679,6 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
-    private int packageFlagsToInstallFlags(PackageSetting ps) {
-        int installFlags = 0;
-        if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
-            // This existing package was an external ASEC install when we have
-            // the external flag without a UUID
-            installFlags |= PackageManager.INSTALL_EXTERNAL;
-        }
-        if (ps.isForwardLocked()) {
-            installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-        }
-        return installFlags;
-    }
-
     private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
         if (isExternal(pkg)) {
             if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -16773,9 +16694,6 @@
     private void deleteTempPackageFiles() {
         final FilenameFilter filter =
                 (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
-        for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
-            file.delete();
-        }
     }
 
     @Override
@@ -17766,7 +17684,7 @@
         // Delete application code and resources only for parent packages
         if (ps.parentPackageName == null) {
             if (deleteCodeAndResources && (outInfo != null)) {
-                outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+                outInfo.args = createInstallArgsForExisting(
                         ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                 if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
             }
@@ -21698,7 +21616,6 @@
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final PackageManager pm = mContext.getPackageManager();
 
-        final boolean currentAsec;
         final String currentVolumeUuid;
         final File codeFile;
         final String installerPackageName;
@@ -21732,22 +21649,13 @@
                         "3rd party apps are not allowed on internal storage");
             }
 
-            if (pkg.applicationInfo.isExternalAsec()) {
-                currentAsec = true;
-                currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
-            } else if (pkg.applicationInfo.isForwardLocked()) {
-                currentAsec = true;
-                currentVolumeUuid = "forward_locked";
-            } else {
-                currentAsec = false;
-                currentVolumeUuid = ps.volumeUuid;
+            currentVolumeUuid = ps.volumeUuid;
 
-                final File probe = new File(pkg.codePath);
-                final File probeOat = new File(probe, "oat");
-                if (!probe.isDirectory() || !probeOat.isDirectory()) {
-                    throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
-                            "Move only supported for modern cluster style installs");
-                }
+            final File probe = new File(pkg.codePath);
+            final File probeOat = new File(probe, "oat");
+            if (!probe.isDirectory() || !probeOat.isDirectory()) {
+                throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                        "Move only supported for modern cluster style installs");
             }
 
             if (Objects.equals(currentVolumeUuid, volumeUuid)) {
@@ -21784,12 +21692,11 @@
         final boolean moveCompleteApp;
         final File measurePath;
 
+        installFlags = INSTALL_INTERNAL;
         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
-            installFlags = INSTALL_INTERNAL;
-            moveCompleteApp = !currentAsec;
+            moveCompleteApp = true;
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
-            installFlags = INSTALL_EXTERNAL;
             moveCompleteApp = false;
             measurePath = storage.getPrimaryPhysicalVolume().getPath();
         } else {
@@ -21801,9 +21708,6 @@
                         "Move location not mounted private volume");
             }
 
-            Preconditions.checkState(!currentAsec);
-
-            installFlags = INSTALL_INTERNAL;
             moveCompleteApp = true;
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6f275ec..77f8c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2193,9 +2193,6 @@
         boolean replaceExisting = true;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "-l":
-                    sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-                    break;
                 case "-r": // ignore
                     break;
                 case "-R":
@@ -2210,9 +2207,6 @@
                 case "-t":
                     sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
                     break;
-                case "-s":
-                    sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
-                    break;
                 case "-f":
                     sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
                     break;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b850613..2c2cc7e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,10 +152,6 @@
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
     }
 
-    public boolean isForwardLocked() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
-    }
-
     public boolean isSystem() {
         return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 239dc99..fbf5439 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,7 +64,6 @@
                 | ApplicationInfo.PRIVATE_FLAG_VENDOR
                 | ApplicationInfo.PRIVATE_FLAG_PRODUCT
                 | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
-                | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6009bd3..c334b6e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3656,7 +3656,6 @@
 
     private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
     private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
-    private static int PRE_M_APP_INFO_FLAG_FORWARD_LOCK = 1<<29;
     private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
 
     private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -3756,15 +3755,11 @@
                     if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
                         pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
                     }
-                    if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
-                        pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
-                    }
                     if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
                         pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
                     }
                     pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
                             | PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
-                            | PRE_M_APP_INFO_FLAG_FORWARD_LOCK
                             | PRE_M_APP_INFO_FLAG_PRIVILEGED);
                 } else {
                     // For backward compatibility
@@ -4428,7 +4423,6 @@
             ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
             ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
             ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
-            ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
             ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
             ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
             ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 774134c..21cc14e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -195,7 +195,6 @@
         // STOPSHIP(b/112545973): remove once feature enabled by default
         if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
             MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
-            MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_AUDIO);
         }
     }
 
@@ -203,10 +202,8 @@
     static {
         // STOPSHIP(b/112545973): remove once feature enabled by default
         if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
-            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
-            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_IMAGES);
             MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
-            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_VIDEO);
+            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
         }
     }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 3179ce9..aa11e1e 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -51,6 +51,7 @@
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -1517,6 +1518,21 @@
         pulledData.add(e);
     }
 
+    private void pullBuildInformation(int tagId,
+            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeString(Build.FINGERPRINT);
+        e.writeString(Build.BRAND);
+        e.writeString(Build.PRODUCT);
+        e.writeString(Build.DEVICE);
+        e.writeString(Build.VERSION.RELEASE);
+        e.writeString(Build.ID);
+        e.writeString(Build.VERSION.INCREMENTAL);
+        e.writeString(Build.TYPE);
+        e.writeString(Build.TAGS);
+        pulledData.add(e);
+    }
+
     private BatteryStatsHelper getBatteryStatsHelper() {
         if (mBatteryStatsHelper == null) {
             final long callingToken = Binder.clearCallingIdentity();
@@ -1810,6 +1826,10 @@
                 pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.BUILD_INFORMATION: {
+                pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             case StatsLog.PROCESS_CPU_TIME: {
                 pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 1eb44a0..361622f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1177,12 +1177,14 @@
     }
 
     @Override
-    public void onNotificationSmartReplySent(String key, int replyIndex)
+    public void onNotificationSmartReplySent(
+            String key, int replyIndex, CharSequence reply, boolean generatedByAssistant)
             throws RemoteException {
         enforceStatusBarService();
         long identity = Binder.clearCallingIdentity();
         try {
-            mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+            mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
+                    generatedByAssistant);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 409d2b4..6ede423 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -88,7 +88,6 @@
 import android.util.Xml;
 import android.view.Display;
 import android.view.IWindowManager;
-import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
@@ -487,6 +486,8 @@
     private void generateCrop(WallpaperData wallpaper) {
         boolean success = false;
 
+        // Only generate crop for default display.
+        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
         Rect cropHint = new Rect(wallpaper.cropHint);
 
         if (DEBUG) {
@@ -494,7 +495,7 @@
                     + Integer.toHexString(wallpaper.whichPending)
                     + " to " + wallpaper.cropFile.getName()
                     + " crop=(" + cropHint.width() + 'x' + cropHint.height()
-                    + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
+                    + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
         }
 
         // Analyse the source; needed in multiple cases
@@ -533,11 +534,11 @@
             }
 
             // scale if the crop height winds up not matching the recommended metrics
-            needScale = (wallpaper.height != cropHint.height());
+            needScale = (wpData.mHeight != cropHint.height());
 
             if (DEBUG) {
                 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
-                Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+                Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
                 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
                 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
             }
@@ -567,7 +568,7 @@
                     // just let the decode take care of it because we also want to remap where the
                     // cropHint rectangle lies in the decoded [super]rect.
                     final BitmapFactory.Options scaler;
-                    final int actualScale = cropHint.height() / wallpaper.height;
+                    final int actualScale = cropHint.height() / wpData.mHeight;
                     int scale = 1;
                     while (2*scale < actualScale) {
                         scale *= 2;
@@ -593,17 +594,18 @@
                         cropHint.offsetTo(0, 0);
                         cropHint.right /= scale;    // adjust by downsampling factor
                         cropHint.bottom /= scale;
-                        final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+                        final float heightR =
+                                ((float) wpData.mHeight) / ((float) cropHint.height());
                         if (DEBUG) {
                             Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
                         }
                         final int destWidth = (int)(cropHint.width() * heightR);
                         final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
-                                destWidth, wallpaper.height, true);
+                                destWidth, wpData.mHeight, true);
                         if (DEBUG) {
                             Slog.v(TAG, "Final extract:");
-                            Slog.v(TAG, "  dims: w=" + wallpaper.width
-                                    + " h=" + wallpaper.height);
+                            Slog.v(TAG, "  dims: w=" + wpData.mWidth
+                                    + " h=" + wpData.mHeight);
                             Slog.v(TAG, "   out: w=" + finalCrop.getWidth()
                                     + " h=" + finalCrop.getHeight());
                         }
@@ -670,13 +672,13 @@
                     if (connector == null) return;
                     connector.disconnectLocked();
                     mLastWallpaper.connection.removeDisplayConnector(displayId);
+                    mLastWallpaper.removeDisplayData(displayId);
                 }
             }
         }
 
         @Override
         public void onDisplayChanged(int displayId) {
-            // TODO(b/115486823) Review that do we need to handle display changes.
         }
     };
 
@@ -778,16 +780,23 @@
         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
                 = new RemoteCallbackList<IWallpaperManagerCallback>();
 
-        int width = -1;
-        int height = -1;
+        private static final class DisplayData {
+            int mWidth = -1;
+            int mHeight = -1;
+            final Rect mPadding = new Rect(0, 0, 0, 0);
+            final int mDisplayId;
+
+            DisplayData(int displayId) {
+                mDisplayId = displayId;
+            }
+        }
+        private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
 
         /**
          * The crop hint supplied for displaying a subset of the source image
          */
         final Rect cropHint = new Rect(0, 0, 0, 0);
 
-        final Rect padding = new Rect(0, 0, 0, 0);
-
         WallpaperData(int userId, String inputFileName, String cropFileName) {
             this.userId = userId;
             final File wallpaperDir = getWallpaperDir(userId);
@@ -803,6 +812,44 @@
         boolean sourceExists() {
             return wallpaperFile.exists();
         }
+
+        void removeDisplayData(int displayId) {
+            mDisplayDatas.remove(displayId);
+        }
+    }
+
+    private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
+        WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+        if (wpdData == null) {
+            wpdData = new WallpaperData.DisplayData(displayId);
+            ensureSaneWallpaperDisplaySize(wpdData, displayId);
+            data.mDisplayDatas.append(displayId, wpdData);
+        }
+        return wpdData;
+    }
+
+    private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
+            int displayId) {
+        // We always want to have some reasonable width hint.
+        final int baseSize = getMaximumSizeDimension(displayId);
+        if (wpdData.mWidth < baseSize) {
+            wpdData.mWidth = baseSize;
+        }
+        if (wpdData.mHeight < baseSize) {
+            wpdData.mHeight = baseSize;
+        }
+    }
+
+    private int getMaximumSizeDimension(int displayId) {
+        Display display = mDisplayManager.getDisplay(displayId);
+        return display.getMaximumSizeDimension();
+    }
+
+    void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
+        for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
+            final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+            action.accept(wpdData);
+        }
     }
 
     int makeWallpaperIdLocked() {
@@ -830,9 +877,11 @@
             }
 
             void ensureStatusHandled() {
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
+                        mDisplayId);
                 if (mDimensionsChanged) {
                     try {
-                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+                        mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set wallpaper dimensions", e);
                     }
@@ -840,7 +889,7 @@
                 }
                 if (mPaddingChanged) {
                     try {
-                        mEngine.setDisplayPadding(mWallpaper.padding);
+                        mEngine.setDisplayPadding(wpdData.mPadding);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set wallpaper padding", e);
                     }
@@ -857,16 +906,16 @@
                     return;
                 }
 
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                        mDisplayId);
                 try {
-                    // TODO(b/115486823) Consider the size of non-default display
                     connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
-                            wallpaper.width, wallpaper.height,
-                            wallpaper.padding, mDisplayId);
+                            wpdData.mWidth, wpdData.mHeight,
+                            wpdData.mPadding, mDisplayId);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed attaching wallpaper on display", e);
-                    // TODO(b/115486823) Failed when attaching a new engine, however, other engines
-                    // may still working. Should we abandon them all or just ignore this one.
-                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+                            && connection.getConnectedEngineSize() == 0) {
                         bindWallpaperComponentLocked(null /* componentName */, false /* force */,
                                 false /* fromUser */, wallpaper, null /* reply */);
                     }
@@ -952,11 +1001,20 @@
 
         void forEachDisplayConnector(Consumer<DisplayConnector> action) {
             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
-                final DisplayConnector connector = mDisplayConnector.get(i);
+                final DisplayConnector connector = mDisplayConnector.valueAt(i);
                 action.accept(connector);
             }
         }
 
+        int getConnectedEngineSize() {
+            int engineSize = 0;
+            for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+                final DisplayConnector connector = mDisplayConnector.valueAt(i);
+                if (connector.mEngine != null) engineSize++;
+            }
+            return engineSize;
+        }
+
         DisplayConnector getDisplayConnectorOrCreate(int displayId) {
             DisplayConnector connector = mDisplayConnector.get(displayId);
             if (connector == null) {
@@ -1128,7 +1186,8 @@
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                // TODO(b/115486823) Extends for secondary display.
+                // TODO(multi-display) So far, we have shared the same wallpaper on each display.
+                // Once we have multiple wallpapers on multiple displays, please complete here.
                 if (displayId == DEFAULT_DISPLAY) {
                     try {
                         // This will trigger onComputeColors in the wallpaper engine.
@@ -1560,7 +1619,7 @@
                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
-                    ensureSaneWallpaperData(fallback);
+                    ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
                     mWaitingForUnlock = true;
                 }
@@ -1705,8 +1764,15 @@
         return false;
     }
 
-    // TODO(b/115486823) Extends this method with specific display.
-    public void setDimensionHints(int width, int height, String callingPackage)
+    private boolean isValidDisplay(int displayId) {
+        return mDisplayManager.getDisplay(displayId) != null;
+    }
+
+    /**
+     * Sets the dimension hint for the wallpaper. These hints indicate the desired
+     * minimum width and height for the wallpaper in a particular display.
+     */
+    public void setDimensionHints(int width, int height, String callingPackage, int displayId)
             throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
         if (!isWallpaperSupported(callingPackage)) {
@@ -1719,90 +1785,113 @@
                 throw new IllegalArgumentException("width and height must be > 0");
             }
 
-            if (width != wallpaper.width || height != wallpaper.height) {
-                wallpaper.width = width;
-                wallpaper.height = height;
-                saveSettingsLocked(userId);
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
+
+            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            if (width != wpdData.mWidth || height != wpdData.mHeight) {
+                wpdData.mWidth = width;
+                wpdData.mHeight = height;
+                if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    // TODO(b/115486823) Extends this method with specific display.
-                    final IWallpaperEngine engine = wallpaper.connection
-                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                            .getDisplayConnectorOrCreate(displayId);
+                    final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
                     if (engine != null) {
                         try {
                             engine.setDesiredSize(width, height);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
-                    } else if (wallpaper.connection.mService != null) {
+                    } else if (wallpaper.connection.mService != null && connector != null) {
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        // TODO(b/115486823) Extends this method with specific display.
-                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
-                                .mDimensionsChanged = true;
+                        connector.mDimensionsChanged = true;
                     }
                 }
             }
         }
     }
 
-    public int getWidthHint() throws RemoteException {
+    /**
+     * Returns the desired minimum width for the wallpaper in a particular display.
+     */
+    public int getWidthHint(int displayId) throws RemoteException {
         synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                return wallpaper.width;
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                        displayId);
+                return wpdData.mWidth;
             } else {
                 return 0;
             }
         }
     }
 
-    public int getHeightHint() throws RemoteException {
+    /**
+     * Returns the desired minimum height for the wallpaper in a particular display.
+     */
+    public int getHeightHint(int displayId) throws RemoteException {
         synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                return wallpaper.height;
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                        displayId);
+                return wpdData.mHeight;
             } else {
                 return 0;
             }
         }
     }
 
-    // TODO(b/115486823) Extends this method with specific display.
-    public void setDisplayPadding(Rect padding, String callingPackage) {
+    /**
+     * Sets extra padding that we would like the wallpaper to have outside of the display.
+     */
+    public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
         if (!isWallpaperSupported(callingPackage)) {
             return;
         }
         synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
             int userId = UserHandle.getCallingUserId();
             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
             if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
                 throw new IllegalArgumentException("padding must be positive: " + padding);
             }
 
-            if (!padding.equals(wallpaper.padding)) {
-                wallpaper.padding.set(padding);
-                saveSettingsLocked(userId);
+            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            if (!padding.equals(wpdData.mPadding)) {
+                wpdData.mPadding.set(padding);
+                if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    // TODO(b/115486823) Extends this method with specific display.
-                    final IWallpaperEngine engine = wallpaper.connection
-                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                            .getDisplayConnectorOrCreate(displayId);
+                    final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
                     if (engine != null) {
                         try {
                             engine.setDisplayPadding(padding);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
-                    } else if (wallpaper.connection.mService != null) {
+                    } else if (wallpaper.connection.mService != null && connector != null) {
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        // TODO(b/115486823) Extends this method with specific display.
-                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
-                                .mPaddingChanged = true;
+                        connector.mPaddingChanged = true;
                     }
                 }
             }
@@ -1850,10 +1939,13 @@
                 // user switch)
                 return null;
             }
+            // Only for default display.
+            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                    DEFAULT_DISPLAY);
             try {
                 if (outParams != null) {
-                    outParams.putInt("width", wallpaper.width);
-                    outParams.putInt("height", wallpaper.height);
+                    outParams.putInt("width", wpdData.mWidth);
+                    outParams.putInt("height", wpdData.mHeight);
                 }
                 if (cb != null) {
                     wallpaper.callbacks.register(cb);
@@ -2075,10 +2167,14 @@
         // We know a-priori that there is no lock-only wallpaper currently
         WallpaperData lockWP = new WallpaperData(userId,
                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+        final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
+                DEFAULT_DISPLAY);
+        final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
+                DEFAULT_DISPLAY);
         lockWP.wallpaperId = sysWP.wallpaperId;
         lockWP.cropHint.set(sysWP.cropHint);
-        lockWP.width = sysWP.width;
-        lockWP.height = sysWP.height;
+        lockWPDData.mWidth = sysWPDData.mWidth;
+        lockWPDData.mHeight = sysWPDData.mHeight;
         lockWP.allowBackup = sysWP.allowBackup;
         lockWP.primaryColors = sysWP.primaryColors;
 
@@ -2478,27 +2574,29 @@
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
+        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                DEFAULT_DISPLAY);
         out.startTag(null, tag);
         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
-        out.attribute(null, "width", Integer.toString(wallpaper.width));
-        out.attribute(null, "height", Integer.toString(wallpaper.height));
+        out.attribute(null, "width", Integer.toString(wpdData.mWidth));
+        out.attribute(null, "height", Integer.toString(wpdData.mHeight));
 
         out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
         out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
         out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
         out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
 
-        if (wallpaper.padding.left != 0) {
-            out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+        if (wpdData.mPadding.left != 0) {
+            out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
         }
-        if (wallpaper.padding.top != 0) {
-            out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+        if (wpdData.mPadding.top != 0) {
+            out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
         }
-        if (wallpaper.padding.right != 0) {
-            out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+        if (wpdData.mPadding.right != 0) {
+            out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
         }
-        if (wallpaper.padding.bottom != 0) {
-            out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+        if (wpdData.mPadding.bottom != 0) {
+            out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
         }
 
         if (wallpaper.primaryColors != null) {
@@ -2601,14 +2699,14 @@
                     wallpaper = new WallpaperData(userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
                     mLockWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper);
+                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
                 } else {
                     // sanity fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
                     mWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper);
+                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
                 }
             }
         }
@@ -2637,6 +2735,8 @@
             }
         }
         boolean success = false;
+        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                DEFAULT_DISPLAY);
         try {
             stream = new FileInputStream(file);
             XmlPullParser parser = Xml.newPullParser();
@@ -2663,8 +2763,8 @@
                         }
 
                         if (DEBUG) {
-                            Slog.v(TAG, "mWidth:" + wallpaper.width);
-                            Slog.v(TAG, "mHeight:" + wallpaper.height);
+                            Slog.v(TAG, "mWidth:" + wpdData.mWidth);
+                            Slog.v(TAG, "mHeight:" + wpdData.mHeight);
                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
                             Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
                             Slog.v(TAG, "mName:" + wallpaper.name);
@@ -2700,10 +2800,10 @@
         IoUtils.closeQuietly(stream);
 
         if (!success) {
-            wallpaper.width = -1;
-            wallpaper.height = -1;
+            wpdData.mWidth = -1;
+            wpdData.mHeight = -1;
             wallpaper.cropHint.set(0, 0, 0, 0);
-            wallpaper.padding.set(0, 0, 0, 0);
+            wpdData.mPadding.set(0, 0, 0, 0);
             wallpaper.name = "";
 
             mLockWallpaperMap.remove(userId);
@@ -2717,26 +2817,22 @@
             }
         }
 
-        ensureSaneWallpaperData(wallpaper);
+        ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
-            ensureSaneWallpaperData(lockWallpaper);
+            ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
         }
     }
 
-    private void ensureSaneWallpaperData(WallpaperData wallpaper) {
-        // We always want to have some reasonable width hint.
-        int baseSize = getMaximumSizeDimension();
-        if (wallpaper.width < baseSize) {
-            wallpaper.width = baseSize;
-        }
-        if (wallpaper.height < baseSize) {
-            wallpaper.height = baseSize;
-        }
-        // and crop, if not previously specified
-        if (wallpaper.cropHint.width() <= 0
-                || wallpaper.cropHint.height() <= 0) {
-            wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
+    private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
+        final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+
+        if (displayId == DEFAULT_DISPLAY) {
+            // crop, if not previously specified
+            if (wallpaper.cropHint.width() <= 0
+                    || wallpaper.cropHint.height() <= 0) {
+                wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
+            }
         }
     }
 
@@ -2752,19 +2848,20 @@
             wallpaper.wallpaperId = makeWallpaperIdLocked();
         }
 
+        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+
         if (!keepDimensionHints) {
-            wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
-            wallpaper.height = Integer.parseInt(parser
-                    .getAttributeValue(null, "height"));
+            wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+            wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
         }
         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
-        wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
-        wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
-        wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
-        wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+        wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
+        wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
+        wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
+        wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
         if (colorsCount > 0) {
             Color primary = null, secondary = null, tertiary = null;
@@ -2787,12 +2884,6 @@
         wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
     }
 
-    private int getMaximumSizeDimension() {
-        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-        Display d = wm.getDefaultDisplay();
-        return d.getMaximumSizeDimension();
-    }
-
     // Called by SystemBackupAgent after files are restored to disk.
     public void settingsRestored() {
         // Verify caller is the system
@@ -2832,7 +2923,7 @@
                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
                         + " id=" + wallpaper.wallpaperId);
                 if (success) {
-                    generateCrop(wallpaper);    // based on the new image + metadata
+                    generateCrop(wallpaper); // based on the new image + metadata
                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
                             wallpaper, null);
                 }
@@ -2937,12 +3028,16 @@
                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
                     pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                pw.print("  mWidth=");
-                    pw.print(wallpaper.width);
-                    pw.print(" mHeight=");
-                    pw.println(wallpaper.height);
+                forEachDisplayData(wallpaper, wpSize -> {
+                    pw.print("  displayId=");
+                    pw.println(wpSize.mDisplayId);
+                    pw.print("  mWidth=");
+                    pw.print(wpSize.mWidth);
+                    pw.print("  mHeight=");
+                    pw.println(wpSize.mHeight);
+                    pw.print("  mPadding="); pw.println(wpSize.mPadding);
+                });
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
-                pw.print("  mPadding="); pw.println(wallpaper.padding);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
@@ -2973,11 +3068,15 @@
             for (int i = 0; i < mLockWallpaperMap.size(); i++) {
                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
-                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                pw.print("  mWidth="); pw.print(wallpaper.width);
-                    pw.print(" mHeight="); pw.println(wallpaper.height);
+                pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                forEachDisplayData(wallpaper, wpSize -> {
+                    pw.print("  displayId=");
+                    pw.println(wpSize.mDisplayId);
+                    pw.print("  mWidth="); pw.print(wpSize.mWidth);
+                    pw.print("  mHeight="); pw.println(wpSize.mHeight);
+                    pw.print("  mPadding="); pw.println(wpSize.mPadding);
+                });
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
-                pw.print("  mPadding="); pw.println(wallpaper.padding);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 694e9d1..c517bd7 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -315,7 +315,7 @@
     /** The number of distinct task ids that can be assigned to the tasks of a single user */
     private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
 
-    ActivityTaskManagerService mService;
+    final ActivityTaskManagerService mService;
 
     /** The historial list of recent tasks including inactive tasks */
     RecentTasks mRecentTasks;
@@ -618,11 +618,6 @@
     }
 
     @VisibleForTesting
-    void setService(ActivityTaskManagerService service) {
-        mService = service;
-    }
-
-    @VisibleForTesting
     void setWindowContainerController(RootWindowContainerController controller) {
         mWindowContainerController = controller;
     }
@@ -1039,11 +1034,8 @@
         boolean didSomething = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isTopDisplayFocusedStack(stack)) {
-                    continue;
-                }
+            final ActivityStack stack = display.getFocusedStack();
+            if (stack != null) {
                 stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
                 final ActivityRecord top = stack.topRunningActivityLocked();
                 final int size = mTmpActivityList.size();
@@ -2429,6 +2421,7 @@
         // We give preference to the launch preference in activity options.
         if (options != null) {
             taskId = options.getLaunchTaskId();
+            displayId = options.getLaunchDisplayId();
         }
 
         // First preference for stack goes to the task Id set in the activity options. Use the stack
@@ -2448,7 +2441,7 @@
         T stack;
 
         // Next preference for stack goes to the display Id set the candidate display.
-        if (launchParams != null) {
+        if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
             displayId = launchParams.mPreferredDisplayId;
         }
         if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
@@ -2566,10 +2559,10 @@
                 windowingMode = options != null ? options.getLaunchWindowingMode()
                         : r.getWindowingMode();
             }
-            return activityDisplay.createStack(
-                    windowingMode,
-                    options != null ? options.getLaunchActivityType() : r.getActivityType(),
-                    true /*onTop*/);
+            final int activityType =
+                    options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
+                            ? options.getLaunchActivityType() : r.getActivityType();
+            return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
         }
 
         Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d4c1bca..90f3ff8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1859,7 +1859,8 @@
             }
         }
 
-        if (mStartActivity.isActivityTypeHome() && intentActivity != null
+        if (intentActivity != null
+                && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
                 && intentActivity.getDisplayId() != mPreferredDisplayId) {
             // Do not reuse home activity on other displays.
             intentActivity = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4f01d699..d0e3fb4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -341,7 +341,8 @@
     ActivityManagerInternal mAmInternal;
     UriGrantsManagerInternal mUgmInternal;
     private PackageManagerInternal mPmInternal;
-    private ActivityTaskManagerInternal mInternal;
+    @VisibleForTesting
+    final ActivityTaskManagerInternal mInternal;
     PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
@@ -643,6 +644,7 @@
         mSystemThread = ActivityThread.currentActivityThread();
         mUiContext = mSystemThread.getSystemUiContext();
         mLifecycleManager = new ClientLifecycleManager();
+        mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
     }
 
@@ -893,7 +895,6 @@
     }
 
     private void start() {
-        mInternal = new LocalService();
         LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
     }
 
@@ -6871,4 +6872,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 92944a0..52c78ce 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1081,8 +1081,12 @@
         super.onDisplayChanged(dc);
         if (prevDc != null && prevDc.mFocusedApp == this) {
             prevDc.setFocusedApp(null);
-            if (dc.getTopStack().getTopChild().getTopChild() == this) {
-                dc.setFocusedApp(this);
+            final TaskStack stack = dc.getTopStack();
+            if (stack != null) {
+                final Task task = stack.getTopChild();
+                if (task != null && task.getTopChild() == this) {
+                    dc.setFocusedApp(this);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 886b2ff..3acacbc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -352,6 +352,14 @@
     int pendingLayoutChanges;
     int mDeferredRotationPauseCount;
 
+    /**
+     * Used to gate application window layout until we have sent the complete configuration.
+     * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+     *       we want to replace this flag with a mechanism that will confirm the configuration
+     *       applied by the client is the one expected by the system server.
+     */
+    boolean mWaitingForConfig;
+
     // TODO(multi-display): remove some of the usages.
     @VisibleForTesting
     boolean isDefaultDisplay;
@@ -1284,7 +1292,7 @@
                 + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
 
         if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
-            mService.mWaitingForConfig = true;
+            mWaitingForConfig = true;
         }
 
         mRotation = rotation;
@@ -3675,6 +3683,7 @@
             }
             mTmpWindow = w;
             w.setDisplayLayoutNeeded();
+            w.finishSeamlessRotation(true /* timeout */);
             mService.markForSeamlessRotation(w, false);
         }, true /* traverseTopToBottom */);
 
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
      * {@link WindowState} next frame, such that they apply at the same time the client draws the
      * window in the new orientation.
+     *
+     * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+     * it.
      */
-    public void finish(WindowState win) {
+    public void finish(WindowState win, boolean timeout) {
         mTransform.reset();
         final Transaction t = win.getPendingTransaction();
         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
         t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
-        if (win.mWinAnimator.mSurfaceController != null) {
+        if (win.mWinAnimator.mSurfaceController != null && !timeout) {
             t.deferTransactionUntil(win.mSurfaceControl,
                     win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
             t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39a8465..25f3128 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -599,7 +599,6 @@
     long mDisplayFreezeTime = 0;
     int mLastDisplayFreezeDuration = 0;
     Object mLastFinishedFreezeSource = null;
-    boolean mWaitingForConfig = false;
     boolean mSwitchingUser = false;
 
     final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -1870,7 +1869,7 @@
         long origId = Binder.clearCallingIdentity();
         final int displayId;
         synchronized (mGlobalLock) {
-            WindowState win = windowForClientLocked(session, client, false);
+            final WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 return 0;
             }
@@ -1885,8 +1884,9 @@
 
             win.setFrameNumber(frameNumber);
 
-            if (!mWaitingForConfig) {
-                win.finishSeamlessRotation();
+            final DisplayContent dc = win.getDisplayContent();
+            if (!dc.mWaitingForConfig) {
+                win.finishSeamlessRotation(false /* timeout */);
             }
 
             int attrChanges = 0;
@@ -2438,7 +2438,7 @@
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             displayContent.computeScreenConfiguration(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
-                mWaitingForConfig = true;
+                displayContent.mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
                 displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
@@ -2451,9 +2451,10 @@
         return config;
     }
 
-    void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
-        if (mWaitingForConfig) {
-            mWaitingForConfig = false;
+    void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+            @NonNull DisplayContent dc) {
+        if (dc.mWaitingForConfig) {
+            dc.mWaitingForConfig = false;
             mLastFinishedFreezeSource = "new-config";
         }
 
@@ -4181,13 +4182,11 @@
                 // placement to unfreeze the display since we froze it when the rotation was updated
                 // in DisplayContent#updateRotationUnchecked.
                 synchronized (mGlobalLock) {
-                    if (mWaitingForConfig) {
-                        mWaitingForConfig = false;
+                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                    if (dc != null && dc.mWaitingForConfig) {
+                        dc.mWaitingForConfig = false;
                         mLastFinishedFreezeSource = "config-unchanged";
-                        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                        if (dc != null) {
-                            dc.setLayoutNeeded();
-                        }
+                        dc.setLayoutNeeded();
                         mWindowPlacerLocked.performSurfacePlacement();
                     }
                 }
@@ -5136,7 +5135,7 @@
         configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
-            mWaitingForConfig = true;
+            displayContent.mWaitingForConfig = true;
             startFreezingDisplayLocked(0 /* exitAnim */,
                     0 /* enterAnim */, displayContent);
             displayContent.sendNewConfiguration();
@@ -5396,23 +5395,24 @@
             return;
         }
 
-        final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
-        if (mWaitingForConfig || mAppsFreezingScreen > 0
+        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+        final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+        final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+        if (waitingForConfig || mAppsFreezingScreen > 0
                 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
-                || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+                || mClientFreezingScreen || numOpeningApps > 0) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
-                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
                 + ", mAppsFreezingScreen=" + mAppsFreezingScreen
                 + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
                 + ", mClientFreezingScreen=" + mClientFreezingScreen
-                + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+                + ", mOpeningApps.size()=" + numOpeningApps);
             return;
         }
 
         if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
                 "stopFreezingDisplayLocked: Unfreezing now");
 
-        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
 
         // We must make a local copy of the displayId as it can be potentially overwritten later on
         // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -6033,7 +6033,6 @@
                     pw.print(" windows="); pw.print(mWindowsFreezingScreen);
                     pw.print(" client="); pw.print(mClientFreezingScreen);
                     pw.print(" apps="); pw.print(mAppsFreezingScreen);
-                    pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
             final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
             pw.print("  mRotation="); pw.print(defaultDisplayContent.getRotation());
                     pw.print(" mAltOrientation=");
@@ -6042,6 +6041,9 @@
                     pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
                     pw.print(" mLastOrientation=");
                             pw.println(defaultDisplayContent.getLastOrientation());
+                    pw.print(" waitingForConfig=");
+                            pw.println(defaultDisplayContent.mWaitingForConfig);
+
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 567b583..6f044f3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -180,6 +180,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
@@ -579,8 +580,8 @@
 
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
 
-    void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
-            boolean requested) {
+    void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+            @Rotation int rotation, boolean requested) {
         // Invisible windows and the wallpaper do not participate in the seamless rotation animation
         if (!isVisibleNow() || mIsWallpaper) {
             return;
@@ -597,9 +598,9 @@
         }
     }
 
-    void finishSeamlessRotation() {
+    void finishSeamlessRotation(boolean timeout) {
         if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.finish(this);
+            mPendingSeamlessRotate.finish(this, timeout);
             mFinishSeamlessRotateFrameNumber = getFrameNumber();
             mPendingSeamlessRotate = null;
             mService.markForSeamlessRotation(this, false);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7193dd7..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -113,7 +113,9 @@
             return;
         }
 
-        if (mService.mWaitingForConfig) {
+        // TODO(multi-display):
+        final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+        if (defaultDisplay.mWaitingForConfig) {
             // Our configuration has changed (most likely rotation), but we
             // don't yet have the complete configuration to report to
             // applications.  Don't do any window layout until we have it.
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 57e954f..08fbf55 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -24,11 +24,14 @@
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.SnapshotData;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.server.AbstractRemoteService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
 
 import java.io.PrintWriter;
@@ -39,12 +42,12 @@
     private static final String TAG = "ContentCaptureSession";
 
     private final Object mLock;
-    private final IBinder mActivityToken;
-
+    final IBinder mActivityToken;
     private final IntelligencePerUserService mService;
     private final RemoteIntelligenceService mRemoteService;
     private final InteractionContext mInterationContext;
     private final InteractionSessionId mId;
+    private AugmentedAutofillCallback mAutofillCallback;
 
     ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
             @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
@@ -92,6 +95,18 @@
     }
 
     /**
+     * Requests the service to autofill the given field.
+     */
+    public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client,
+            int autofillSessionId, @NonNull AutofillId focusedId) {
+        mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId);
+        if (mAutofillCallback == null) {
+            mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId);
+        }
+        return mAutofillCallback;
+    }
+
+    /**
      * Cleans up the session and removes it from the service.
      *
      * @param notifyRemoteService whether it should trigger a {@link
@@ -119,6 +134,11 @@
         if (mService.isVerbose()) {
             Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
         }
+        if (mAutofillCallback != null) {
+            mAutofillCallback.destroy();
+            mAutofillCallback = null;
+        }
+
         // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
         if (notifyRemoteService) {
             mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
@@ -152,6 +172,8 @@
         pw.print(prefix); pw.print("id: ");  mId.dump(pw); pw.println();
         pw.print(prefix); pw.print("context: ");  mInterationContext.dump(pw); pw.println();
         pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+        pw.print(prefix); pw.print("has autofill callback: ");
+        pw.println(mAutofillCallback != null);
     }
 
     @Override
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index a7f45ee..38810dd 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -27,6 +27,8 @@
 import android.os.IBinder;
 import android.os.UserManager;
 import android.service.intelligence.InteractionSessionId;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IIntelligenceManager;
 
@@ -86,20 +88,6 @@
         service.destroyLocked();
     }
 
-    /**
-     * Notifies the intelligence service of new assist data for the given activity.
-     *
-     * @return {@code false} if there was no service set for the given user
-     */
-    private boolean sendActivityAssistDataLocked(@UserIdInt int userId,
-            @NonNull IBinder activityToken, @NonNull Bundle data) {
-        final IntelligencePerUserService service = peekServiceForUserLocked(userId);
-        if (service != null) {
-            return service.sendActivityAssistDataLocked(activityToken, data);
-        }
-        return false;
-    }
-
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -112,7 +100,7 @@
     final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
 
         @Override
-        public void startSession(int userId, @NonNull IBinder activityToken,
+        public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
                 @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
                 int flags, @NonNull IResultReceiver result) {
             Preconditions.checkNotNull(activityToken);
@@ -134,7 +122,7 @@
         }
 
         @Override
-        public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+        public void sendEvents(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
                 @NonNull List<ContentCaptureEvent> events) {
             Preconditions.checkNotNull(sessionId);
             Preconditions.checkNotNull(events);
@@ -146,7 +134,7 @@
         }
 
         @Override
-        public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+        public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId) {
             Preconditions.checkNotNull(sessionId);
 
             synchronized (mLock) {
@@ -168,14 +156,13 @@
     private final class LocalService extends IntelligenceManagerInternal {
 
         @Override
-        public boolean isIntelligenceServiceForUser(int uid, int userId) {
+        public boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId) {
             synchronized (mLock) {
                 final IntelligencePerUserService service = peekServiceForUserLocked(userId);
                 if (service != null) {
                     return service.isIntelligenceServiceForUserLocked(uid);
                 }
             }
-
             return false;
         }
 
@@ -183,8 +170,26 @@
         public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
                 @NonNull Bundle data) {
             synchronized (mLock) {
-                return sendActivityAssistDataLocked(userId, activityToken, data);
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.sendActivityAssistDataLocked(activityToken, data);
+                }
             }
+            return false;
+        }
+
+        @Override
+        public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+                @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+                int autofillSessionId, @NonNull AutofillId focusedId) {
+            synchronized (mLock) {
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.requestAutofill(client, activityToken, autofillSessionId,
+                            focusedId);
+                }
+            }
+            return null;
         }
     }
 }
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 9694ab9..051f0d6 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -22,6 +22,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -36,12 +37,15 @@
 import android.service.intelligence.SnapshotData;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IntelligenceManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.server.AbstractPerUserSystemService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -62,7 +66,7 @@
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
     protected IntelligencePerUserService(
-            IntelligenceManagerService master, Object lock, int userId) {
+            IntelligenceManagerService master, Object lock, @UserIdInt int userId) {
         super(master, lock, userId);
     }
 
@@ -210,6 +214,17 @@
         return uid == getServiceUidLocked();
     }
 
+    @GuardedBy("mLock")
+    private ContentCaptureSession getSession(@NonNull IBinder activityToken) {
+        for (int i = 0; i < mSessions.size(); i++) {
+            final ContentCaptureSession session = mSessions.valueAt(i);
+            if (session.mActivityToken.equals(activityToken)) {
+                return session;
+            }
+        }
+        return null;
+    }
+
     /**
      * Destroys the service and all state associated with it.
      *
@@ -226,6 +241,22 @@
         mSessions.clear();
     }
 
+    public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
+            @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
+        synchronized (mLock) {
+            final ContentCaptureSession session = getSession(activityToken);
+            if (session != null) {
+                // TODO(b/111330312): log metrics
+                if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()");
+                return session.requestAutofillLocked(client, autofillSessionId, focusedId);
+            }
+            if (mMaster.debug) {
+                Slog.d(TAG, "requestAutofill(): no session for " + activityToken);
+            }
+            return null;
+        }
+    }
+
     @Override
     protected void dumpLocked(String prefix, PrintWriter pw) {
         super.dumpLocked(prefix, pw);
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index a27c1cf..00c5b6a 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
@@ -28,8 +29,12 @@
 import android.service.intelligence.SnapshotData;
 import android.text.format.DateUtils;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
+import com.android.internal.os.IResultReceiver;
 import com.android.server.AbstractRemoteService;
 
 import java.util.List;
@@ -39,7 +44,7 @@
     private static final String TAG = "RemoteIntelligenceService";
 
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
     private final RemoteIntelligenceServiceCallbacks mCallbacks;
     private IIntelligenceService mService;
@@ -101,6 +106,25 @@
         scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
     }
 
+    /**
+     * Called by {@link ContentCaptureSession} to request augmented autofill.
+     */
+    public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId,
+            @NonNull IAutoFillManagerClient client, int autofillSessionId,
+            @NonNull AutofillId focusedId) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId,
+                focusedId));
+    }
+
+    /**
+     * Called by {@link ContentCaptureSession} when it's time to destroy all augmented autofill
+     * requests.
+     */
+    public void onDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+    }
 
     private abstract static class MyPendingRequest
             extends PendingRequest<RemoteIntelligenceService> {
@@ -124,8 +148,9 @@
             final RemoteIntelligenceService remoteService = getService();
             if (remoteService != null) {
                 try {
-                    myRun(remoteService);
                     // We don't expect the service to call us back, so we finish right away.
+                    myRun(remoteService);
+                    // TODO(b/111330312): not true anymore!!
                     finish();
                 } catch (RemoteException e) {
                     Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
@@ -191,6 +216,53 @@
         }
     }
 
+    private static final class PendingAutofillRequest extends MyPendingRequest {
+        private final @NonNull AutofillId mFocusedId;
+        private final @NonNull IAutoFillManagerClient mClient;
+        private final int mAutofillSessionId;
+
+        protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client,
+                int autofillSessionId, @NonNull AutofillId focusedId) {
+            super(service, sessionId);
+            mClient = client;
+            mAutofillSessionId = autofillSessionId;
+            mFocusedId = focusedId;
+        }
+
+        @Override // from MyPendingRequest
+        public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+            final IResultReceiver receiver = new IResultReceiver.Stub() {
+
+                @Override
+                public void send(int resultCode, Bundle resultData) throws RemoteException {
+                    final IBinder realClient = resultData
+                            .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
+                    remoteService.mService.onAutofillRequest(mSessionId, realClient,
+                            mAutofillSessionId, mFocusedId);
+                }
+            };
+
+            // TODO(b/111330312): set cancellation signal, timeout (from  both mClient and service),
+            // cache IAugmentedAutofillManagerClient reference, etc...
+            mClient.getAugmentedAutofillClient(receiver);
+        }
+    }
+
+    private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
+
+        protected PendingDestroyAutofillWindowsRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId) {
+            super(service, sessionId);
+        }
+
+        @Override
+        protected void myRun(@NonNull RemoteIntelligenceService service) throws RemoteException {
+            service.mService.onDestroyAutofillWindowsRequest(mSessionId);
+            // TODO(b/111330312): implement timeout
+        }
+    }
+
     public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
         // To keep it simple, we use the same callback for all failures / timeouts.
         void onFailureOrTimeout(boolean timedOut);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3266b8b..fcd29e1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,10 +38,8 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.os.Build.VERSION_CODES.O_MR1;
 import static android.os.Build.VERSION_CODES.P;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -68,16 +66,15 @@
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
-import android.app.Application;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
 import android.app.Notification;
 import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
-import android.app.ITransientNotification;
-import android.app.IUriGrantsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
@@ -100,7 +97,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.MediaStore;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
@@ -116,7 +112,6 @@
 import android.text.Html;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
-import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -288,6 +283,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
         when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+        when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
 
         // write to a test file; the system file isn't readable from tests
         mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -1735,7 +1731,8 @@
     }
 
     @Test
-    public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+    public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
+            throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
@@ -2509,6 +2506,7 @@
 
         mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+        verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
     }
 
     @Test
@@ -2517,8 +2515,11 @@
         mService.addNotification(r);
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
     }
 
@@ -2529,8 +2530,12 @@
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(
+                eq(r.sbn), eq(false), eq((false)));
     }
 
     @Test
@@ -3459,11 +3464,12 @@
         ApplicationInfo info = new ApplicationInfo();
         info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
 
-        assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+        assertTrue(mService.isCallerInstantApp(45770, 0));
 
         info.privateFlags = 0;
-        assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+        assertFalse(mService.isCallerInstantApp(575370, 0));
     }
 
     @Test
@@ -3472,8 +3478,9 @@
         info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
 
-        assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+        assertTrue(mService.isCallerInstantApp(68638450, 10));
     }
 
     @Test
@@ -3689,4 +3696,19 @@
                 new TestableToastCallback(), 2000, 0);
         assertEquals(1, mService.mToastQueue.size());
     }
+
+    @Test
+    public void testOnNotificationSmartReplySent() {
+        final int replyIndex = 2;
+        final String reply = "Hello";
+        final boolean generatedByAssistant = true;
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationSmartReplySent(
+                r.getKey(), replyIndex, reply, generatedByAssistant);
+        verify(mAssistants).notifyAssistantSuggestedReplySent(
+                eq(r.sbn), eq(reply), eq(generatedByAssistant));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index caabdbd..c2ab3ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -35,6 +35,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
@@ -66,6 +67,8 @@
 import com.android.server.AttributeCache;
 import com.android.server.ServiceThread;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentController;
+import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.After;
@@ -115,27 +118,13 @@
     }
 
     ActivityTaskManagerService createActivityTaskManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        setupActivityManagerService(atm);
-        return atm;
+        mService = new TestActivityTaskManagerService(mContext);
+        mSupervisor = mService.mStackSupervisor;
+        return mService;
     }
 
     void setupActivityTaskManagerService() {
-        mService = createActivityTaskManagerService();
-        mSupervisor = mService.mStackSupervisor;
-    }
-
-    ActivityManagerService createActivityManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        return setupActivityManagerService(atm);
-    }
-
-    ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
-        setupActivityManagerService(am, atm);
-        return am;
+        createActivityTaskManagerService();
     }
 
     /** Creates a {@link TestActivityDisplay}. */
@@ -154,32 +143,6 @@
         return display;
     }
 
-    void setupActivityManagerService(
-            TestActivityManagerService am, TestActivityTaskManagerService atm) {
-        atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
-        atm.mAmInternal = am.getLocalService();
-        am.mAtmInternal = atm.getLocalService();
-        // Makes sure the supervisor is using with the spy object.
-        atm.mStackSupervisor.setService(atm);
-        doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
-        doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
-        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
-        doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
-        doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
-        doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        am.mActivityTaskManager = atm;
-        am.mWindowManager = prepareMockWindowManager();
-        atm.setWindowManager(am.mWindowManager);
-
-        // Put a home stack on the default display, so that we'll always have something focusable.
-        final TestActivityStackSupervisor supervisor =
-                (TestActivityStackSupervisor) atm.mStackSupervisor;
-        supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
-                .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
-        new ActivityBuilder(atm).setTask(task).build();
-    }
-
     /**
      * Builder for creating new activities.
      */
@@ -405,23 +368,48 @@
         }
     }
 
-    protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
-        private LockTaskController mLockTaskController;
-        private ActivityTaskManagerInternal mInternal;
+    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
         private PackageManagerInternal mPmInternal;
 
         // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
         // We keep the reference in order to prevent creating it twice.
-        private ActivityStackSupervisor mTestStackSupervisor;
+        ActivityStackSupervisor mTestStackSupervisor;
 
         TestActivityTaskManagerService(Context context) {
             super(context);
+            spyOn(this);
+
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
             mSupportsMultiWindow = true;
             mSupportsMultiDisplay = true;
             mSupportsSplitScreenMultiWindow = true;
             mSupportsFreeformWindowManagement = true;
             mSupportsPictureInPicture = true;
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+            final TestActivityManagerService am =
+                    new TestActivityManagerService(mTestInjector, this);
+
+            // Put a home stack on the default display, so that we'll always have something
+            // focusable.
+            final TestActivityStackSupervisor supervisor =
+                    (TestActivityStackSupervisor) mStackSupervisor;
+            supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+            final TaskRecord task = new TaskBuilder(mStackSupervisor)
+                    .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
+            new ActivityBuilder(this).setTask(task).build();
+
+            spyOn(getLifecycleManager());
+            spyOn(getLockTaskController());
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+        }
+
+        void setActivityManagerService(IntentFirewall intentFirewall,
+                PendingIntentController intentController, ActivityManagerInternal amInternal,
+                WindowManagerService wm) {
+            mAmInternal = amInternal;
+            setActivityManagerService(intentFirewall, intentController);
+            setWindowManager(wm);
         }
 
         @Override
@@ -430,54 +418,17 @@
         }
 
         @Override
-        public LockTaskController getLockTaskController() {
-            if (mLockTaskController == null) {
-                mLockTaskController = spy(super.getLockTaskController());
-            }
-
-            return mLockTaskController;
-        }
-
-        @Override
         void updateUsageStats(ActivityRecord component, boolean resumed) {
         }
 
         @Override
-        protected final ActivityStackSupervisor createStackSupervisor() {
+        protected ActivityStackSupervisor createStackSupervisor() {
             if (mTestStackSupervisor == null) {
-                final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
-                final KeyguardController keyguardController = mock(KeyguardController.class);
-
-                // Invoked during {@link ActivityStack} creation.
-                doNothing().when(supervisor).updateUIDsPresentOnDisplay();
-                // Always keep things awake.
-                doReturn(true).when(supervisor).hasAwakeDisplay();
-                // Called when moving activity to pinned stack.
-                doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
-                        anyBoolean());
-                // Do not schedule idle timeouts
-                doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
-                // unit test version does not handle launch wake lock
-                doNothing().when(supervisor).acquireLaunchWakelock();
-                doReturn(keyguardController).when(supervisor).getKeyguardController();
-
-                supervisor.initialize();
-                mTestStackSupervisor = supervisor;
+                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
             }
             return mTestStackSupervisor;
         }
 
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new TestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-        ActivityTaskManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new ActivityTaskManagerService.LocalService();
-            }
-            return mInternal;
-        }
-
         @Override
         PackageManagerInternal getPackageManagerInternalLocked() {
             if (mPmInternal == null) {
@@ -524,24 +475,31 @@
         }
     }
 
+    // TODO: Replace this with a mock object since we are no longer in AMS package.
     /**
      * An {@link ActivityManagerService} subclass which provides a test
      * {@link ActivityStackSupervisor}.
      */
-    static class TestActivityManagerService extends ActivityManagerService {
+    class TestActivityManagerService extends ActivityManagerService {
 
-        private ActivityManagerInternal mInternal;
-
-        TestActivityManagerService(TestInjector testInjector) {
+        TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
             super(testInjector, testInjector.mHandlerThread);
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-        }
+            spyOn(this);
 
-        ActivityManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new LocalService();
-            }
-            return mInternal;
+            mWindowManager = prepareMockWindowManager();
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+            atm.setActivityManagerService(mIntentFirewall, mPendingIntentController,
+                    new LocalService(), mWindowManager);
+
+            mActivityTaskManager = atm;
+            mAtmInternal = atm.mInternal;
+
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+            doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
+            doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+            doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
         }
     }
 
@@ -549,23 +507,40 @@
      * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
      * setup not available in the test environment. Also specifies an injector for
      */
-    protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
+    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
         private ActivityDisplay mDisplay;
         private KeyguardController mKeyguardController;
 
-        public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
             super(service, looper);
+            spyOn(this);
             mDisplayManager =
                     (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
             mWindowManager = prepareMockWindowManager();
             mKeyguardController = mock(KeyguardController.class);
             setWindowContainerController(mock(RootWindowContainerController.class));
+
+            // Invoked during {@link ActivityStack} creation.
+            doNothing().when(this).updateUIDsPresentOnDisplay();
+            // Always keep things awake.
+            doReturn(true).when(this).hasAwakeDisplay();
+            // Called when moving activity to pinned stack.
+            doNothing().when(this).ensureActivitiesVisibleLocked(any(), anyInt(),
+                    anyBoolean());
+            // Do not schedule idle timeouts
+            doNothing().when(this).scheduleIdleTimeoutLocked(any());
+            // unit test version does not handle launch wake lock
+            doNothing().when(this).acquireLaunchWakelock();
+            doReturn(mKeyguardController).when(this).getKeyguardController();
+
+            initialize();
         }
 
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
+            mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+            spyOn(mDisplay);
             addChild(mDisplay, ActivityDisplay.POSITION_TOP);
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 2f3f698..8596c77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -110,9 +110,7 @@
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
-        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
-        setupActivityManagerService(am, mTestService);
+        mTestService = new MyTestActivityTaskManagerService(mContext);
         mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
         mRecentTasks.loadParametersFromResources(mContext.getResources());
         mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
@@ -868,20 +866,11 @@
         }
 
         @Override
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new MyTestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-    }
-
-    private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService() {
-            super(mTestInjector);
-        }
-
-        @Override
-        public boolean isUserRunning(int userId, int flags) {
-            return true;
+        protected ActivityStackSupervisor createStackSupervisor() {
+            if (mTestStackSupervisor == null) {
+                mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
+            }
+            return mTestStackSupervisor;
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 50190e7..070f073 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -25,6 +25,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -50,61 +51,50 @@
 public class RecentsAnimationTest extends ActivityTestsBase {
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private TestActivityTaskManagerService mTestService;
     private ComponentName mRecentsComponent;
 
     @Before
     public void setUp() throws Exception {
         mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
-        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
-        setupActivityManagerService(mTestService);
+        mService = new TestActivityTaskManagerService(mContext);
+
+        final RecentTasks recentTasks = mService.getRecentTasks();
+        spyOn(recentTasks);
+        mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+        doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
     }
 
     @Test
     public void testCancelAnimationOnStackOrderChange() {
         ActivityStack fullscreenStack =
-                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                mService.mStackSupervisor.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+        ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+        ActivityRecord recentsActivity = new ActivityBuilder(mService)
                 .setComponent(mRecentsComponent)
                 .setCreateTask(true)
                 .setStack(recentsStack)
                 .build();
         ActivityStack fullscreenStack2 =
-                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                mService.mStackSupervisor.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+        ActivityRecord fsActivity = new ActivityBuilder(mService)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
                 .setCreateTask(true)
                 .setStack(fullscreenStack2)
                 .build();
-        doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+        doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
 
         // Start the recents animation
         Intent recentsIntent = new Intent();
         recentsIntent.setComponent(mRecentsComponent);
-        mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+        mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
 
         fullscreenStack.moveToFront("Activity start");
 
         // Ensure that the recents animation was canceled
-        verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
                 eq(REORDER_KEEP_IN_PLACE), any());
     }
-
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            RecentTasks recents = mock(RecentTasks.class);
-            doReturn(mRecentsComponent).when(recents).getRecentsComponent();
-            System.out.println(mRecentsComponent);
-            return recents;
-        }
-    }
 }
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 152831f..4f573a4 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,8 +27,6 @@
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -846,8 +844,6 @@
             // Inform listeners if necessary
             if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
                     || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
-                    || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START
-                    || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP
                     || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION
                     || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -900,10 +896,6 @@
         switch (eventType) {
             case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
             case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
-            case UsageEvents.Event.FOREGROUND_SERVICE_START:
-                return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-            case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
-                return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
             case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
             case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d940620..01e566c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,6 +20,7 @@
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
 
@@ -89,11 +90,23 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-        stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
-                parser, LAST_TIME_SERVICE_USED_ATTR);
+
+        try {
+            stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+                    parser, LAST_TIME_SERVICE_USED_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+        }
+
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
-        stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
+
+        try {
+            stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
                 TOTAL_TIME_SERVICE_USED_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+        }
+
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
         stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR,
                 0);
@@ -350,8 +363,17 @@
         }
 
         statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
-        statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
-        statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+        try {
+            statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse majorVersion", e);
+        }
+
+        try {
+            statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse minorVersion", e);
+        }
 
         int eventCode;
         int outerDepth = parser.getDepth();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5ad7c30..185c886 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1838,6 +1838,13 @@
             "notify_international_call_on_wfc_bool";
 
     /**
+     * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN,
+     * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN.
+     * Default value is false.
+     */
+    public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
+
+    /**
      * Flag specifying whether to show an alert dialog for video call charges.
      * By default this value is {@code false}.
      * @hide
@@ -2643,6 +2650,7 @@
         sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
                 false);
         sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
         sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
                 null);
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index e373797..269cda1 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -37,6 +37,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -107,14 +108,14 @@
      * {@link MbmsGroupCallSession} that you received before calling this method again.
      *
      * @param context The {@link Context} to use.
-     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The subscription ID to use.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
      * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
      */
     public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
-            @NonNull Executor executor, int subscriptionId,
+            int subscriptionId, @NonNull Executor executor,
             final @NonNull MbmsGroupCallSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
@@ -138,11 +139,11 @@
 
     /**
      * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
-     * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+     * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
      */
     public static MbmsGroupCallSession create(@NonNull Context context,
             @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
-        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+        return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
     }
 
     /**
@@ -153,7 +154,7 @@
      * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+     * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
      * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
      * returns.
      *
@@ -189,18 +190,19 @@
      * Asynchronous errors through the callback include any of the errors in
      * {@link MbmsErrors.GeneralErrors}.
      *
-     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param tmgi The TMGI, an identifier for the group call you want to join.
-     * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+     * @param saiList A list of SAIs for the group call that should be negotiated separately with
      *                the carrier.
-     * @param frequencyArray An array of frequencies for the group call that should be negotiated
+     * @param frequencyList A lost of frequencies for the group call that should be negotiated
      *                separately with the carrier.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param callback The callback that you want to receive information about the call on.
      * @return An instance of {@link GroupCall} through which the call can be controlled.
      *         May be {@code null} if an error occurred.
      */
-    public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
-            int[] frequencyArray, @NonNull GroupCallCallback callback) {
+    public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+            @NonNull GroupCallCallback callback) {
         IMbmsGroupCallService groupCallService = mService.get();
         if (groupCallService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -215,7 +217,7 @@
 
         try {
             int returnCode = groupCallService.startGroupCall(
-                    mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+                    mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
             if (returnCode == MbmsErrors.UNKNOWN) {
                 // Unbind and throw an obvious error
                 close();
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index bdba8c8..2d46ec2 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,8 +17,8 @@
 package android.telephony.emergency;
 
 import android.annotation.IntDef;
-import android.hardware.radio.V1_3.EmergencyNumberSource;
-import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.hardware.radio.V1_4.EmergencyNumberSource;
+import android.hardware.radio.V1_4.EmergencyServiceCategory;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -138,7 +138,7 @@
     }
 
     /**
-     * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+     * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
      *
      * The emergency number has one or more defined emergency number sources.
      *
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
index 9aca18e..25e274e 100644
--- a/telephony/java/android/telephony/mbms/GroupCall.java
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.RemoteException;
 import android.telephony.MbmsGroupCallSession;
 import android.telephony.mbms.vendor.IMbmsGroupCallService;
@@ -24,6 +25,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * Class used to represent a single MBMS group call. After a call has been started with
@@ -41,8 +43,26 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
     public @interface GroupCallState {}
+
+    /**
+     * Indicates that the group call is in a stopped state
+     *
+     * This can be reported after network action or after calling {@link #close}.
+     */
     public static final int STATE_STOPPED = 1;
+
+    /**
+     * Indicates that the group call is started.
+     *
+     * Data can be transmitted and received in this state.
+     */
     public static final int STATE_STARTED = 2;
+
+    /**
+     * Indicates that the group call is stalled.
+     *
+     * This may be due to a network issue or the device being temporarily out of range.
+     */
     public static final int STATE_STALLED = 3;
 
     /**
@@ -122,16 +142,17 @@
      * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
      * information of the group call has * changed. Callers must obtain this information from the
      * wireless carrier independently.
-     * @param saiArray New array of SAIs that the call is available on.
-     * @param frequencyArray New array of frequencies that the call is available on.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
      */
-    public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+    public void updateGroupCall(@NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList) {
         if (mService == null) {
             throw new IllegalStateException("No group call service attached");
         }
 
         try {
-            mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+            mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 001bb02..77e36bb 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.Nullable;
 
 import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
  * A callback class for use when the application is in a group call. The middleware
  * will provide updates on the status of the call via this callback.
  */
-public class GroupCallCallback {
+public interface GroupCallCallback {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
@@ -40,7 +41,7 @@
             MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
             MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
             MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
-    private @interface GroupCallError{}
+    @interface GroupCallError{}
 
     /**
      * Indicates broadcast signal strength is not available for this call.
@@ -48,7 +49,7 @@
      * This may be due to the call no longer being available due to geography
      * or timing (end of service)
      */
-    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+    int SIGNAL_STRENGTH_UNAVAILABLE = -1;
 
     /**
      * Called by the middleware when it has detected an error condition in this group call. The
@@ -56,9 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(@GroupCallError int errorCode, @Nullable String message) {
-        // default implementation empty
-    }
+    void onError(@GroupCallError int errorCode, @Nullable String message);
 
     /**
      * Called to indicate this call has changed state.
@@ -66,10 +65,8 @@
      * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
      * and {@link GroupCall#STATE_STALLED}.
      */
-    public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
-            @GroupCall.GroupCallStateChangeReason int reason) {
-        // default implementation empty
-    }
+    void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason);
 
     /**
      * Broadcast Signal Strength updated.
@@ -81,7 +78,5 @@
      * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
      * for this call due to timing, geography or popularity.
      */
-    public void onBroadcastSignalStrengthUpdated(int signalStrength) {
-        // default implementation empty
-    }
+    void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 7c4321b..52e4d33 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -140,5 +140,21 @@
         public static final int ERROR_UNKNOWN_FILE_INFO = 403;
     }
 
+    /**
+     * Indicates the errors that are applicable only to the group call use-case.
+     */
+    public static class GroupCallErrors {
+        private GroupCallErrors() { }
+        /** Indicates that the middleware was unable to start the group call. */
+        public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+        /**
+         * Indicates that the app called
+         * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+         * same {@code tmgi}.
+         */
+        public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+    }
+
     private MbmsErrors() {}
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 7da734e..04e7ba1 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.telephony.MbmsGroupCallSession;
@@ -29,9 +30,9 @@
 /**
  * A callback class that is used to receive information from the middleware on MBMS group-call
  * services. An instance of this object should be passed into
- * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
  */
-public class MbmsGroupCallSessionCallback {
+public interface MbmsGroupCallSessionCallback {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
@@ -48,7 +49,7 @@
             MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
             MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
             MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
-    private @interface GroupCallError{}
+    @interface GroupCallError{}
 
     /**
      * Called by the middleware when it has detected an error condition. The possible error codes
@@ -56,8 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(@GroupCallError int errorCode, @Nullable String message) {
-    }
+    void onError(@GroupCallError int errorCode, @Nullable String message);
 
     /**
      * Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,21 +70,22 @@
      * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
      *                      contains the available SAIs in an individual cell.
      */
-    public void onAvailableSaisUpdated(List<Integer> currentSais,
-            List<List<Integer>> availableSais) {
-    }
+    void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+            @NonNull List<List<Integer>> availableSais);
 
     /**
      * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
-     * via this callback may be used to establish a data-link interface with the modem before the
-     * middleware is ready.
-     * Note that this method may be called before {@link #onMiddlewareReady()}.
+     * via this callback may be used to establish a data-link interface with the modem.
+     *
+     * In order to establish the data-link interface, the multicast IP and port must be obtained
+     * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+     * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+     * callback.
      *
      * @param interfaceName The interface name for the data link.
      * @param index The index for the data link.
      */
-    public void onServiceInterfaceAvailable(String interfaceName, int index) {
-    }
+    void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
 
     /**
      * Called to indicate that the middleware has been initialized and is ready.
@@ -94,6 +95,5 @@
      * delivered via {@link #onError(int, String)} with error code
      * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
      */
-    public void onMiddlewareReady() {
-    }
+    void onMiddlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 721256a..44cc24a 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,11 +29,11 @@
 
     void stopGroupCall(int subId, long tmgi);
 
-    void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
-        in int[] frequencyArray);
+    void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
+        in List frequencyList);
 
-    int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
-        in int[] frequencyArray, IGroupCallCallback callback);
+    int startGroupCall(int subscriptionId, long tmgi, in List saiList,
+        in List frequencyList, IGroupCallCallback callback);
 
     void dispose(int subId);
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 3734ca7..e86a47d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -115,15 +115,16 @@
         }
 
         @Override
-        public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
-                int[] frequencyArray) {
+        public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+                List frequencyList) {
             MbmsGroupCallServiceBase.this.updateGroupCall(
-                    subscriptionId, tmgi, saiArray, frequencyArray);
+                    subscriptionId, tmgi, saiList, frequencyList);
         }
 
         @Override
-        public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
-                final int[] frequencyArray, final IGroupCallCallback callback)
+        public int startGroupCall(final int subscriptionId, final long tmgi,
+                final List saiList,
+                final List frequencyList, final IGroupCallCallback callback)
                 throws RemoteException {
             if (callback == null) {
                 throw new NullPointerException("Callback must not be null");
@@ -132,7 +133,7 @@
             final int uid = Binder.getCallingUid();
 
             int result = MbmsGroupCallServiceBase.this.startGroupCall(
-                    subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+                    subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
                         @Override
                         public void onError(final int errorCode, final String message) {
                             try {
@@ -209,13 +210,13 @@
      *
      * @param subscriptionId The subscription id to use.
      * @param tmgi The TMGI, an identifier for the group call.
-     * @param saiArray An array of SAIs for the group call.
-     * @param frequencyArray An array of frequencies for the group call.
+     * @param saiList A list of SAIs for the group call.
+     * @param frequencyList A list of frequencies for the group call.
      * @param callback The callback object on which the app wishes to receive updates.
      * @return Any error in {@link MbmsErrors.GeneralErrors}
      */
-    public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
-            GroupCallCallback callback) {
+    public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+            List<Integer> frequencyList, GroupCallCallback callback) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
@@ -237,11 +238,11 @@
     /**
      * Called when the app receives new SAI and frequency information for the group call identified
      * by {@code tmgi}.
-     * @param saiArray New array of SAIs that the call is available on.
-     * @param frequencyArray New array of frequencies that the call is available on.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
      */
-    public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
-            int[] frequencyArray) {
+    public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+            List<Integer> frequencyList) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index af7123b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -27,9 +27,17 @@
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.spy;
@@ -40,6 +48,8 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -48,12 +58,19 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PermissionMonitorTest {
-    private static final int MOCK_UID = 10001;
-    private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+    private static final int MOCK_USER1 = 0;
+    private static final int MOCK_USER2 = 1;
+    private static final int MOCK_UID1 = 10001;
+    private static final String MOCK_PACKAGE1 = "appName1";
+    private static final String SYSTEM_PACKAGE1 = "sysName1";
+    private static final String SYSTEM_PACKAGE2 = "sysName2";
     private static final String PARTITION_SYSTEM = "system";
     private static final String PARTITION_OEM = "oem";
     private static final String PARTITION_PRODUCT = "product";
@@ -63,6 +80,7 @@
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
+    @Mock private INetworkManagementService mNMS;
 
     private PermissionMonitor mPermissionMonitor;
 
@@ -70,8 +88,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
-        mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
     }
 
     private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -80,7 +97,8 @@
         packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
         packageInfo.applicationInfo.uid = uid;
         when(mPackageManager.getPackageInfoAsUser(
-                eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+                eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
         return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
     }
 
@@ -143,16 +161,16 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
     }
 
     @Test
@@ -172,15 +190,150 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
-        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+    }
+
+    private class NMSMonitor {
+        private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+        NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+            // Add hook to verify and track result of setPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                final Boolean isSystem = args[0].equals("SYSTEM");
+                for (final int uid : (int[]) args[1]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+                    //     fail("uid " + uid + " is already set to " + isSystem);
+                    // }
+                    mApps.put(uid, isSystem);
+                }
+                return null;
+            }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+            // Add hook to verify and track result of clearPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                for (final int uid : (int[]) args[0]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (!mApps.containsKey(uid)) {
+                    //     fail("uid " + uid + " does not exist.");
+                    // }
+                    mApps.remove(uid);
+                }
+                return null;
+            }).when(mockNMS).clearPermission(any(int[].class));
+        }
+
+        public void expectPermission(Boolean permission, int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (!mApps.containsKey(uid)) {
+                        fail("uid " + uid + " does not exist.");
+                    }
+                    if (mApps.get(uid) != permission) {
+                        fail("uid " + uid + " has wrong permission: " +  permission);
+                    }
+                }
+            }
+        }
+
+        public void expectNoPermission(int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (mApps.containsKey(uid)) {
+                        fail("uid " + uid + " has listed permissions, expected none.");
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testUserAndPackageAddRemove() throws Exception {
+        final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+        // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE1));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE2));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(MOCK_PACKAGE1));
+
+        // Add SYSTEM_PACKAGE2, expect only have network permission.
+        mPermissionMonitor.onUserAdded(MOCK_USER1);
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        // Add SYSTEM_PACKAGE1, expect permission escalate.
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{MOCK_UID1});
+
+        // Remove MOCK_UID1, expect no permission left for all user.
+        mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+        // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserRemoved(MOCK_USER1);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+        // Remove all packages, expect no permission left.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+        removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+
+        // Remove last user, expect no redundant clearPermission is invoked.
+        mPermissionMonitor.onUserRemoved(MOCK_USER2);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+    }
+
+    // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+    // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+    // called multiple times with the uid corresponding to each user.
+    private void addPackageForUsers(int[] users, String packageName, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+        }
+    }
+
+    private void removePackageForUsers(int[] users, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+        }
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index aa8d325..87706b9 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -60,14 +60,27 @@
      */
     private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
     /**
+     * Whether this is an OWE network or not.
+     */
+    private boolean mIsEnhancedOpen;
+    /**
      * Pre-shared key for use with WPA-PSK networks.
      */
-    private @Nullable String mPskPassphrase;
+    private @Nullable String mWpa2PskPassphrase;
+    /**
+     * Pre-shared key for use with WPA3-SAE networks.
+     */
+    private @Nullable String mWpa3SaePassphrase;
     /**
      * The enterprise configuration details specifying the EAP method,
-     * certificates and other settings associated with the EAP.
+     * certificates and other settings associated with the WPA-EAP networks.
      */
-    private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+    private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
+    /**
+     * The enterprise configuration details specifying the EAP method,
+     * certificates and other settings associated with the SuiteB networks.
+     */
+    private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
     /**
      * This is a network that does not broadcast its SSID, so an
      * SSID-specific probe request must be used for scans.
@@ -94,8 +107,11 @@
     public WifiNetworkConfigBuilder() {
         mSsidPatternMatcher = null;
         mBssidPatternMatcher = null;
-        mPskPassphrase = null;
-        mEnterpriseConfig = null;
+        mIsEnhancedOpen = false;
+        mWpa2PskPassphrase = null;
+        mWpa3SaePassphrase = null;
+        mWpa2EnterpriseConfig = null;
+        mWpa3EnterpriseConfig = null;
         mIsHiddenSSID = false;
         mIsAppInteractionRequired = false;
         mIsUserInteractionRequired = false;
@@ -188,36 +204,81 @@
     }
 
     /**
-     * Set the ASCII PSK passphrase for this network. Needed for authenticating to
-     * WPA_PSK networks.
+     * Specifies whether this represents an Enhanced Open (OWE) network.
      *
-     * @param pskPassphrase PSK passphrase of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsEnhancedOpen() {
+        mIsEnhancedOpen = true;
+        return this;
+    }
+
+    /**
+     * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to
+     * WPA2-PSK networks.
+     *
+     * @param passphrase passphrase of the network.
      * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
      * method.
      * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
      */
-    public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
-        checkNotNull(pskPassphrase);
+    public WifiNetworkConfigBuilder setWpa2Passphrase(@NonNull String passphrase) {
+        checkNotNull(passphrase);
         final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
-        if (!asciiEncoder.canEncode(pskPassphrase)) {
+        if (!asciiEncoder.canEncode(passphrase)) {
             throw new IllegalArgumentException("passphrase not ASCII encodable");
         }
-        mPskPassphrase = pskPassphrase;
+        mWpa2PskPassphrase = passphrase;
+        return this;
+    }
+
+    /**
+     * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to
+     * WPA3-SAE networks.
+     *
+     * @param passphrase passphrase of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+     */
+    public WifiNetworkConfigBuilder setWpa3Passphrase(@NonNull String passphrase) {
+        checkNotNull(passphrase);
+        final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+        if (!asciiEncoder.canEncode(passphrase)) {
+            throw new IllegalArgumentException("passphrase not ASCII encodable");
+        }
+        mWpa3SaePassphrase = passphrase;
         return this;
     }
 
     /**
      * Set the associated enterprise configuration for this network. Needed for authenticating to
-     * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+     * WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description.
      *
      * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
      * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
      * method.
      */
-    public WifiNetworkConfigBuilder setEnterpriseConfig(
+    public WifiNetworkConfigBuilder setWpa2EnterpriseConfig(
             @NonNull WifiEnterpriseConfig enterpriseConfig) {
         checkNotNull(enterpriseConfig);
-        mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+        mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+        return this;
+    }
+
+    /**
+     * Set the associated enterprise configuration for this network. Needed for authenticating to
+     * WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description.
+     *
+     * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setWpa3EnterpriseConfig(
+            @NonNull WifiEnterpriseConfig enterpriseConfig) {
+        checkNotNull(enterpriseConfig);
+        mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
         return this;
     }
 
@@ -324,16 +385,38 @@
         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
     }
 
-    private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
-        if (!TextUtils.isEmpty(mPskPassphrase)) {
-            // WPA_PSK network.
+    private void setSecurityParamsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+        if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
-        } else if (mEnterpriseConfig != null) {
-            // WPA_EAP network
+            // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+            configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
+        } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+            // PMF mandatory for SAE.
+            configuration.requirePMF = true;
+            // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+            configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
+        } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
-        } else {
-            // Open network
+            configuration.enterpriseConfig = mWpa2EnterpriseConfig;
+        } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+            configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+            // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
+            configuration.allowedGroupMgmtCiphers.set(
+                    WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
+            configuration.allowedSuiteBCiphers.set(
+                    WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+            configuration.allowedSuiteBCiphers.set(
+                    WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+            configuration.requirePMF = true;
+            configuration.enterpriseConfig = mWpa3EnterpriseConfig;
+        } else if (mIsEnhancedOpen) { // OWE network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+            // PMF mandatory.
+            configuration.requirePMF = true;
+        } else { // Open network
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         }
     }
@@ -349,12 +432,7 @@
         if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
             wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
         }
-        setKeyMgmtInWifiConfiguration(wifiConfiguration);
-        // WifiConfiguration.preSharedKey needs quotes around ASCII password.
-        if (mPskPassphrase != null) {
-            wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
-        }
-        wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+        setSecurityParamsInWifiConfiguration(wifiConfiguration);
         wifiConfiguration.hiddenSSID = mIsHiddenSSID;
         wifiConfiguration.priority = mPriority;
         wifiConfiguration.meteredOverride =
@@ -396,6 +474,20 @@
         return false;
     }
 
+    private void validateSecurityParams() {
+        int numSecurityTypes = 0;
+        numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
+        numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0;
+        numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
+        numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
+        numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
+        if (numSecurityTypes > 1) {
+            throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
+                    + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig"
+                    + " can be invoked for network specifier");
+        }
+    }
+
     /**
      * Create a specifier object used to request a Wi-Fi network. The generated
      * {@link NetworkSpecifier} should be used in
@@ -464,10 +556,7 @@
                     + "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
                     + "specifier");
         }
-        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
-            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
-                    + " be invoked for network specifier");
-        }
+        validateSecurityParams();
 
         return new WifiNetworkSpecifier(
                 mSsidPatternMatcher,
@@ -493,10 +582,7 @@
             throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
                     + " allowed for suggestion");
         }
-        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
-            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
-                    + "be invoked for suggestion");
-        }
+        validateSecurityParams();
 
         return new WifiNetworkSuggestion(
                 buildWifiConfiguration(),
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 8980ddb..c455c6f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
@@ -81,11 +82,11 @@
      * pattern.
      */
     @Test
-    public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+    public void testWifiNetworkSpecifierBuilderForWpa2PskNetworkWithBssidPattern() {
         NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
                 .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                         MacAddress.fromString(TEST_BSSID_OUI_MASK))
-                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .buildNetworkSpecifier();
 
         assertTrue(specifier instanceof WifiNetworkSpecifier);
@@ -119,7 +120,7 @@
      * SSID and BSSID pattern.
      */
     @Test
-    public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+    public void testWifiNetworkSpecifierBuilderForWpa2EapHiddenNetworkWithSsidAndBssid() {
         WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
         enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
         enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
@@ -127,7 +128,7 @@
         NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
                 .setBssid(MacAddress.fromString(TEST_BSSID))
-                .setEnterpriseConfig(enterpriseConfig)
+                .setWpa2EnterpriseConfig(enterpriseConfig)
                 .setIsHiddenSsid()
                 .buildNetworkSpecifier();
 
@@ -174,14 +175,14 @@
     }
 
     /**
-     * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+     * Ensure {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} throws an exception
      * when the string is not ASCII encodable.
      */
     @Test(expected = IllegalArgumentException.class)
-    public void testSetPskPassphraseWithNonAsciiString() {
+    public void testSetWpa2PasphraseWithNonAsciiString() {
         new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
-                .setPskPassphrase("salvē")
+                .setWpa2Passphrase("salvē")
                 .buildNetworkSpecifier();
     }
 
@@ -275,15 +276,15 @@
 
     /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
-     * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
-     * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+     * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
      */
     @Test(expected = IllegalStateException.class)
-    public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+    public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndEnterpriseConfig() {
         new WifiNetworkConfigBuilder()
                 .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
-                .setPskPassphrase(TEST_PRESHARED_KEY)
-                .setEnterpriseConfig(new WifiEnterpriseConfig())
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
+                .setWpa2EnterpriseConfig(new WifiEnterpriseConfig())
                 .buildNetworkSpecifier();
     }
 
@@ -375,10 +376,11 @@
      * app interaction and has a priority of zero set.
      */
     @Test
-    public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+    public void
+            testWifiNetworkSuggestionBuilderForWpa2EapNetworkWithPriorityAndReqAppInteraction() {
         WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
-                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .setIsAppInteractionRequired()
                 .setPriority(0)
                 .buildNetworkSuggestion();
@@ -401,10 +403,11 @@
      * user interaction and is metered.
      */
     @Test
-    public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+    public void
+            testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithMeteredAndReqUserInteraction() {
         WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
-                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .setIsUserInteractionRequired()
                 .setIsMetered()
                 .buildNetworkSuggestion();
@@ -422,6 +425,74 @@
     }
 
     /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setIsEnhancedOpen()
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.OWE));
+        assertNull(suggestion.wifiConfiguration.preSharedKey);
+        assertTrue(suggestion.wifiConfiguration.requirePMF);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SAE network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3PskNetwork() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SAE));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                suggestion.wifiConfiguration.preSharedKey);
+        assertTrue(suggestion.wifiConfiguration.requirePMF);
+    }
+
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SuiteB network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.GCMP_256));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers
+                .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+        assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+                .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
+        assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+                .get(WifiConfiguration.SuiteBCipher.ECDHE_RSA));
+        assertTrue(suggestion.wifiConfiguration.requirePMF);
+        assertNull(suggestion.wifiConfiguration.preSharedKey);
+    }
+
+    /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
      * when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
      */
@@ -478,4 +549,46 @@
                 .setPriority(-1)
                 .buildNetworkSuggestion();
     }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndWpa3Passphrase() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setIsEnhancedOpen(} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnhancedOpen() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .setIsEnhancedOpen()
+                .buildNetworkSpecifier();
+    }
 }