Merge "Do not linkify text with RLO/LRO characters."
diff --git a/Android.bp b/Android.bp
index 7e038ce8..090b3c0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,6 +92,7 @@
         "core/java/android/app/IWallpaperManagerCallback.aidl",
         "core/java/android/app/admin/IDeviceAdminService.aidl",
         "core/java/android/app/admin/IDevicePolicyManager.aidl",
+        "core/java/android/app/admin/StartInstallingUpdateCallback.aidl",
         "core/java/android/app/trust/IStrongAuthTracker.aidl",
         "core/java/android/app/trust/ITrustManager.aidl",
         "core/java/android/app/trust/ITrustListener.aidl",
@@ -347,6 +348,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",
@@ -723,6 +725,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",
     ],
@@ -858,7 +861,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 754f12a..a3524da 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;
   }
@@ -6598,6 +6599,7 @@
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
+    method public void installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallUpdateCallback);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isAffiliatedUser();
@@ -6840,6 +6842,16 @@
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
 
+  public static abstract class DevicePolicyManager.InstallUpdateCallback {
+    ctor public DevicePolicyManager.InstallUpdateCallback();
+    method public void onInstallUpdateError(int, java.lang.String);
+    field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
+    field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
+    field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2
+    field public static final int UPDATE_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3; // 0x3
+  }
+
   public static abstract interface DevicePolicyManager.OnClearApplicationUserDataListener {
     method public abstract void onApplicationUserDataCleared(java.lang.String, boolean);
   }
@@ -10118,6 +10130,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";
@@ -22628,7 +22641,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 {
@@ -22640,7 +22653,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();
@@ -22677,7 +22690,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);
@@ -22685,7 +22698,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";
@@ -22704,9 +22717,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 {
@@ -22935,7 +22948,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);
@@ -24575,6 +24588,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";
@@ -29117,15 +29131,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 {
@@ -42871,6 +42888,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";
@@ -43218,9 +43236,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 {
@@ -44225,7 +44243,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
@@ -44237,11 +44255,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
   }
 
@@ -44281,6 +44298,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
@@ -44293,12 +44315,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 {
@@ -49088,7 +49109,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);
@@ -49304,6 +49325,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);
@@ -51896,6 +51919,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 {
@@ -54158,6 +54182,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 0d8ed44..2b41927 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -441,10 +441,10 @@
   }
 
   public class KeyguardManager {
-    method public void setPrivateNotificationsAllowed(boolean);
-    method public boolean getPrivateNotificationsAllowed();
     method public android.content.Intent createConfirmFactoryResetCredentialIntent(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+    method public boolean getPrivateNotificationsAllowed();
     method public void requestDismissKeyguard(android.app.Activity, java.lang.CharSequence, android.app.KeyguardManager.KeyguardDismissCallback);
+    method public void setPrivateNotificationsAllowed(boolean);
   }
 
   public class Notification implements android.os.Parcelable {
@@ -4937,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";
   }
 
@@ -4964,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();
@@ -5023,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 {
@@ -7022,9 +7079,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 {
@@ -7516,7 +7573,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/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index a826ec7..3723fce 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
 package com.android.commands.bmgr;
 
 import android.annotation.IntDef;
+import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
@@ -29,6 +30,7 @@
 import android.app.backup.ISelectBackupTransportCallback;
 import android.app.backup.RestoreSet;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.Bundle;
@@ -37,8 +39,10 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -49,20 +53,34 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
-public final class Bmgr {
-    IBackupManager mBmgr;
-    IRestoreSession mRestore;
+/**
+ * Adb shell command for {@link android.app.backup.IBackupManager}.
+ */
+public class Bmgr {
+    public static final String TAG = "Bmgr";
 
-    static final String BMGR_NOT_RUNNING_ERR =
+    private final IBackupManager mBmgr;
+    private IRestoreSession mRestore;
+
+    private static final String BMGR_NOT_RUNNING_ERR =
             "Error: Could not access the Backup Manager.  Is the system running?";
-    static final String TRANSPORT_NOT_RUNNING_ERR =
+    private static final String TRANSPORT_NOT_RUNNING_ERR =
             "Error: Could not access the backup transport.  Is the system running?";
-    static final String PM_NOT_RUNNING_ERR =
+    private static final String PM_NOT_RUNNING_ERR =
             "Error: Could not access the Package Manager.  Is the system running?";
 
     private String[] mArgs;
     private int mNextArg;
 
+    @VisibleForTesting
+    Bmgr(IBackupManager bmgr) {
+        mBmgr = bmgr;
+    }
+
+    Bmgr() {
+        mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+    }
+
     public static void main(String[] args) {
         try {
             new Bmgr().run(args);
@@ -78,71 +96,73 @@
             return;
         }
 
-        if (!isBmgrActive()) {
+        mArgs = args;
+        mNextArg = 0;
+        int userId = parseUserId();
+        String op = nextArg();
+        Slog.v(TAG, "Running " + op + " for user:" + userId);
+
+        if (!isBmgrActive(userId)) {
             return;
         }
 
-        mArgs = args;
-        String op = args[0];
-        mNextArg = 1;
-
         if ("enabled".equals(op)) {
-            doEnabled();
+            doEnabled(userId);
             return;
         }
 
         if ("enable".equals(op)) {
-            doEnable();
+            doEnable(userId);
             return;
         }
 
         if ("run".equals(op)) {
-            doRun();
+            doRun(userId);
             return;
         }
 
         if ("backup".equals(op)) {
-            doBackup();
+            doBackup(userId);
             return;
         }
 
         if ("init".equals(op)) {
-            doInit();
+            doInit(userId);
             return;
         }
 
         if ("list".equals(op)) {
-            doList();
+            doList(userId);
             return;
         }
 
         if ("restore".equals(op)) {
-            doRestore();
+            doRestore(userId);
             return;
         }
 
         if ("transport".equals(op)) {
-            doTransport();
+            doTransport(userId);
             return;
         }
 
         if ("wipe".equals(op)) {
-            doWipe();
+            doWipe(userId);
             return;
         }
 
         if ("fullbackup".equals(op)) {
-            doFullTransportBackup();
+            doFullTransportBackup(userId);
             return;
         }
 
         if ("backupnow".equals(op)) {
-            doBackupNow();
+            doBackupNow(userId);
             return;
         }
 
         if ("cancel".equals(op)) {
-            doCancel();
+            doCancel(userId);
             return;
         }
 
@@ -155,15 +175,14 @@
         showUsage();
     }
 
-    private boolean isBmgrActive() {
-        mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+    boolean isBmgrActive(@UserIdInt int userId) {
         if (mBmgr == null) {
             System.err.println(BMGR_NOT_RUNNING_ERR);
             return false;
         }
 
         try {
-            if (!mBmgr.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+            if (!mBmgr.isBackupServiceActive(userId)) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return false;
             }
@@ -180,7 +199,7 @@
         return enabled ? "enabled" : "disabled";
     }
 
-    private void doEnabled() {
+    private void doEnabled(@UserIdInt int userId) {
         try {
             boolean isEnabled = mBmgr.isBackupEnabled();
             System.out.println("Backup Manager currently "
@@ -191,7 +210,7 @@
         }
     }
 
-    private void doEnable() {
+    private void doEnable(@UserIdInt int userId) {
         String arg = nextArg();
         if (arg == null) {
             showUsage();
@@ -211,7 +230,7 @@
         }
     }
 
-    private void doRun() {
+    void doRun(@UserIdInt int userId) {
         try {
             mBmgr.backupNow();
         } catch (RemoteException e) {
@@ -220,7 +239,7 @@
         }
     }
 
-    private void doBackup() {
+    private void doBackup(@UserIdInt int userId) {
         String pkg = nextArg();
         if (pkg == null) {
             showUsage();
@@ -235,7 +254,7 @@
         }
     }
 
-    private void doFullTransportBackup() {
+    private void doFullTransportBackup(@UserIdInt int userId) {
         System.out.println("Performing full transport backup");
 
         String pkg;
@@ -354,8 +373,8 @@
         }
     }
 
-    private void backupNowAllPackages(boolean nonIncrementalBackup, @Monitor int monitorState) {
-        int userId = UserHandle.USER_SYSTEM;
+    private void backupNowAllPackages(@UserIdInt int userId, boolean nonIncrementalBackup,
+            @Monitor int monitorState) {
         IPackageManager mPm =
                 IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
         if (mPm == null) {
@@ -379,11 +398,13 @@
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
             }
-            backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState);
+            backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup,
+                    monitorState);
         }
     }
 
     private void backupNowPackages(
+            @UserIdInt int userId,
             List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) {
         int flags = 0;
         if (nonIncrementalBackup) {
@@ -412,7 +433,7 @@
         }
     }
 
-    private void doBackupNow() {
+    private void doBackupNow(@UserIdInt int userId) {
         String pkg;
         boolean backupAll = false;
         boolean nonIncrementalBackup = false;
@@ -439,20 +460,20 @@
             if (allPkgs.size() == 0) {
                 System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                         "incremental backup for all packages.");
-                backupNowAllPackages(nonIncrementalBackup, monitor);
+                backupNowAllPackages(userId, nonIncrementalBackup, monitor);
             } else {
                 System.err.println("Provide only '--all' flag or list of packages.");
             }
         } else if (allPkgs.size() > 0) {
             System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                     "incremental backup for " + allPkgs.size() +" requested packages.");
-            backupNowPackages(allPkgs, nonIncrementalBackup, monitor);
+            backupNowPackages(userId, allPkgs, nonIncrementalBackup, monitor);
         } else {
             System.err.println("Provide '--all' flag or list of packages.");
         }
     }
 
-    private void doCancel() {
+    private void doCancel(@UserIdInt int userId) {
         String arg = nextArg();
         if ("backups".equals(arg)) {
             try {
@@ -467,7 +488,7 @@
         System.err.println("Unknown command.");
     }
 
-    private void doTransport() {
+    private void doTransport(@UserIdInt int userId) {
         try {
             String which = nextArg();
             if (which == null) {
@@ -531,7 +552,7 @@
         }
     }
 
-    private void doWipe() {
+    private void doWipe(@UserIdInt int userId) {
         String transport = nextArg();
         if (transport == null) {
             showUsage();
@@ -563,7 +584,7 @@
         }
     }
 
-    private void doInit() {
+    private void doInit(@UserIdInt int userId) {
         ArraySet<String> transports = new ArraySet<>();
         String transport;
         while ((transport = nextArg()) != null) {
@@ -586,7 +607,7 @@
         }
     }
 
-    private void doList() {
+    private void doList(@UserIdInt int userId) {
         String arg = nextArg();     // sets, transports, packages set#
         if ("transports".equals(arg)) {
             doListTransports();
@@ -603,8 +624,6 @@
 
             if ("sets".equals(arg)) {
                 doListRestoreSets();
-            } else if ("transports".equals(arg)) {
-                doListTransports();
             }
 
             mRestore.endRestoreSession();
@@ -717,7 +736,7 @@
         }
     }
 
-    private void doRestore() {
+    private void doRestore(@UserIdInt int userId) {
         String arg = nextArg();
         if (arg == null) {
             showUsage();
@@ -830,8 +849,18 @@
         return arg;
     }
 
+    private int parseUserId() {
+        String arg = nextArg();
+        if ("--user".equals(arg)) {
+            return UserHandle.parseUserArg(nextArg());
+        } else {
+            mNextArg--;
+            return UserHandle.USER_SYSTEM;
+        }
+    }
+
     private static void showUsage() {
-        System.err.println("usage: bmgr [backup|restore|list|transport|run]");
+        System.err.println("usage: bmgr [--user <userId>] [backup|restore|list|transport|run]");
         System.err.println("       bmgr backup PACKAGE");
         System.err.println("       bmgr enable BOOL");
         System.err.println("       bmgr enabled");
@@ -847,6 +876,10 @@
         System.err.println("       bmgr cancel backups");
         System.err.println("       bmgr init TRANSPORT...");
         System.err.println("");
+        System.err.println("The '--user' option specifies the user on which the operation is run.");
+        System.err.println("It must be the first argument before the operation.");
+        System.err.println("The default value is 0 which is the system user.");
+        System.err.println("");
         System.err.println("The 'backup' command schedules a backup pass for the named package.");
         System.err.println("Note that the backup pass will effectively be a no-op if the package");
         System.err.println("does not actually have changed data to store.");
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..ada5d4c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2421,8 +2421,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;
 }
 
 /**
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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 805fb68..41166dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -157,7 +157,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -5324,16 +5323,6 @@
         }
     }
 
-    /**
-     * Updates the application info.
-     *
-     * This only works in the system process. Must be called on the main thread.
-     */
-    public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
-        Preconditions.checkState(mSystemThread, "Must only be called in the system process");
-        handleApplicationInfoChanged(ai);
-    }
-
     void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
         // Updates triggered by package installation go through a package update
         // receiver. Here we try to capture ApplicationInfo changes that are
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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 24ee7f7..00c1863 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,6 +53,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -87,6 +88,7 @@
 import com.android.org.conscrypt.TrustedCertificateStore;
 
 import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1930,6 +1932,48 @@
     public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
 
     /**
+     * Callback used in {@link #installSystemUpdate} to indicate that there was an error while
+     * trying to install an update.
+     */
+    public abstract static class InstallUpdateCallback {
+        /** Represents an unknown error while trying to install an update. */
+        public static final int UPDATE_ERROR_UNKNOWN = 1;
+
+        /** Represents the update file being intended for different OS version. */
+        public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2;
+
+        /**
+         * Represents the update file being wrong, i.e. payloads are mismatched, wrong compressions
+         * method.
+         */
+        public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
+
+        /** Represents that the file could not be found. */
+        public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4;
+
+        /** Represents the battery being too low to apply an update. */
+        public static final int UPDATE_ERROR_BATTERY_LOW = 5;
+
+        /** Method invoked when there was an error while installing an update. */
+        public void onInstallUpdateError(
+                @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "UPDATE_ERROR_" }, value = {
+            InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+            InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
+            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+            InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+            InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallUpdateCallbackErrorConstants {}
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -6796,7 +6840,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface CreateAndManageUserFlags {}
 
-
     /**
      * Called by a device owner to create a user with the specified name and a given component of
      * the calling package as profile owner. The UserHandle returned by this method should not be
@@ -9827,6 +9870,62 @@
     }
 
     /**
+     * Called by device owner to install a system update from the given file. The device will be
+     * rebooted in order to finish installing the update. Note that if the device is rebooted, this
+     * doesn't necessarily mean that the update has been applied successfully. The caller should
+     * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
+     * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot,
+     * the caller will be notified by {@link InstallUpdateCallback}. If device does not have
+     * sufficient battery level, the installation will fail with error {@link
+     * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
+     *
+     * @param admin The {@link DeviceAdminReceiver} that this request is associated with.
+     * @param updateFilePath An Uri of the file that contains the update. The file should be
+     * readable by the calling app.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback A callback object that will inform the caller when installing an update
+     * fails.
+     */
+    public void installSystemUpdate(
+            @NonNull ComponentName admin, @NonNull Uri updateFilePath,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull InstallUpdateCallback callback) {
+        throwIfParentInstance("installUpdate");
+        if (mService == null) {
+            return;
+        }
+        try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver()
+                    .openFileDescriptor(updateFilePath, "r")) {
+            mService.installUpdateFromFile(
+                    admin, fileDescriptor, new StartInstallingUpdateCallback.Stub() {
+                        @Override
+                        public void onStartInstallingUpdateError(
+                                int errorCode, String errorMessage) {
+                            executeCallback(errorCode, errorMessage, executor, callback);
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (FileNotFoundException e) {
+            Log.w(TAG, e);
+            executeCallback(
+                    InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e),
+                    executor, callback);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            executeCallback(
+                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
+                    executor, callback);
+        }
+    }
+
+    private void executeCallback(int errorCode, String errorMessage,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull InstallUpdateCallback callback) {
+        executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage));
+    }
+
+    /**
      * Returns the system-wide Private DNS mode.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 918c127..60f79d6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -20,6 +20,7 @@
 import android.app.admin.NetworkEvent;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.PasswordMetrics;
@@ -419,4 +420,6 @@
     String getGlobalPrivateDnsHost(in ComponentName admin);
 
     void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
+
+    void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener);
 }
diff --git a/core/java/android/app/admin/StartInstallingUpdateCallback.aidl b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
new file mode 100644
index 0000000..df04707
--- /dev/null
+++ b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** Copyright 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.app.admin;
+
+/**
+* Callback used between {@link DevicePolicyManager} and {@link DevicePolicyManagerService} to
+* indicate that starting installing an update is finished.
+* {@hide}
+*/
+oneway interface StartInstallingUpdateCallback {
+    void onStartInstallingUpdateError(int errorCode, String errorMessage);
+}
\ No newline at end of file
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/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 949cdd6..437039d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,7 +42,6 @@
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.DeadObjectException;
@@ -3244,18 +3243,8 @@
         Objects.requireNonNull(uri);
         Objects.requireNonNull(size);
 
-        // Older apps might be relying on mutable results, so only consider
-        // giving them hardware bitmaps once they target Q or higher. If modern
-        // apps need mutable thumbnails, they can always roll their own logic.
-        final int allocator;
-        if (getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
-            allocator = ImageDecoder.ALLOCATOR_DEFAULT;
-        } else {
-            allocator = ImageDecoder.ALLOCATOR_SOFTWARE;
-        }
-
         try (ContentProviderClient client = acquireContentProviderClient(uri)) {
-            return loadThumbnail(client, uri, size, signal, allocator);
+            return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
         }
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 10c6bc6..6bfddb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1767,6 +1767,9 @@
      * that should be managed by the launched UI.
      * </p>
      * <p>
+     * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+     * </p>
+     * <p>
      * Output: Nothing.
      * </p>
      *
@@ -3586,6 +3589,17 @@
     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";
+
     /**
      * 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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2fcf1dd..2c2776b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2532,55 +2532,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);
-                }
             }
         }
 
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 8599f47..3552655 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -65,10 +65,13 @@
     // An interval, in seconds between the NattKeepalive packets
     private int mNattKeepaliveInterval;
 
-    // XFRM mark and mask
+    // XFRM mark and mask; defaults to 0 (no mark/mask)
     private int mMarkValue;
     private int mMarkMask;
 
+    // XFRM interface id
+    private int mXfrmInterfaceId;
+
     /** Set the mode for this IPsec transform */
     public void setMode(int mode) {
         mMode = mode;
@@ -125,14 +128,30 @@
         mNattKeepaliveInterval = interval;
     }
 
+    /**
+     * Sets the mark value
+     *
+     * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+     * ignored.
+     */
     public void setMarkValue(int mark) {
         mMarkValue = mark;
     }
 
+    /**
+     * Sets the mark mask
+     *
+     * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+     * ignored.
+     */
     public void setMarkMask(int mask) {
         mMarkMask = mask;
     }
 
+    public void setXfrmInterfaceId(int xfrmInterfaceId) {
+        mXfrmInterfaceId = xfrmInterfaceId;
+    }
+
     // Transport or Tunnel
     public int getMode() {
         return mMode;
@@ -190,6 +209,10 @@
         return mMarkMask;
     }
 
+    public int getXfrmInterfaceId() {
+        return mXfrmInterfaceId;
+    }
+
     // Parcelable Methods
 
     @Override
@@ -213,6 +236,7 @@
         out.writeInt(mNattKeepaliveInterval);
         out.writeInt(mMarkValue);
         out.writeInt(mMarkMask);
+        out.writeInt(mXfrmInterfaceId);
     }
 
     @VisibleForTesting
@@ -235,6 +259,7 @@
         mNattKeepaliveInterval = c.mNattKeepaliveInterval;
         mMarkValue = c.mMarkValue;
         mMarkMask = c.mMarkMask;
+        mXfrmInterfaceId = c.mXfrmInterfaceId;
     }
 
     private IpSecConfig(Parcel in) {
@@ -255,6 +280,7 @@
         mNattKeepaliveInterval = in.readInt();
         mMarkValue = in.readInt();
         mMarkMask = in.readInt();
+        mXfrmInterfaceId = in.readInt();
     }
 
     @Override
@@ -289,6 +315,8 @@
                 .append(mMarkValue)
                 .append(", mMarkMask=")
                 .append(mMarkMask)
+                .append(", mXfrmInterfaceId=")
+                .append(mXfrmInterfaceId)
                 .append("}");
 
         return strBuilder.toString();
@@ -320,10 +348,10 @@
                 && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
                 && lhs.mSpiResourceId == rhs.mSpiResourceId
                 && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
-                && IpSecAlgorithm.equals(
-                        lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+                && IpSecAlgorithm.equals(lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
                 && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)
                 && lhs.mMarkValue == rhs.mMarkValue
-                && lhs.mMarkMask == rhs.mMarkMask);
+                && lhs.mMarkMask == rhs.mMarkMask
+                && lhs.mXfrmInterfaceId == rhs.mXfrmInterfaceId);
     }
 }
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/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 16d454d..e032c18 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1232,7 +1232,7 @@
     public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
             Point size, CancellationSignal signal) throws IOException {
         return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
-                ImageDecoder.ALLOCATOR_DEFAULT);
+                ImageDecoder.ALLOCATOR_SOFTWARE);
     }
 
     /**
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/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/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ec63cd9..7815864 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1009,7 +1009,7 @@
                 Log.e(TAG, "null synthesis text");
                 return false;
             }
-            if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) {
+            if (mText.length() > TextToSpeech.getMaxSpeechInputLength()) {
                 Log.w(TAG, "Text too long: " + mText.length() + " chars");
                 return false;
             }
@@ -1609,7 +1609,7 @@
             synchronized (mCallerToCallback) {
                 mCallerToCallback.remove(caller);
             }
-            //mSynthHandler.stopForApp(caller);
+            mSynthHandler.stopForApp(caller);
         }
 
         @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5f1336f..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 */
@@ -3993,7 +3978,7 @@
      *
      * @see #getParent()
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected ViewParent mParent;
 
     /**
@@ -4199,7 +4184,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mLeft;
     /**
      * The distance in pixels from the left edge of this view's parent
@@ -4207,7 +4192,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mRight;
     /**
      * The distance in pixels from the top edge of this view's parent
@@ -4215,7 +4200,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mTop;
     /**
      * The distance in pixels from the top edge of this view's parent
@@ -4223,7 +4208,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mBottom;
 
     /**
@@ -4714,7 +4699,7 @@
      * of this view to at least this amount.
      */
     @ViewDebug.ExportedProperty(category = "measurement")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMinHeight;
 
     /**
@@ -4722,7 +4707,7 @@
      * of this view to at least this amount.
      */
     @ViewDebug.ExportedProperty(category = "measurement")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMinWidth;
 
     /**
@@ -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/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..74678df 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,7 +40,6 @@
 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.util.IndentingPrintWriter;
@@ -58,7 +57,6 @@
 import java.time.Instant;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -269,17 +267,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 +371,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 +514,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 +525,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 +552,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) {
@@ -698,53 +728,67 @@
         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;
                 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 +845,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 +874,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 +912,17 @@
                                     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()));
+        }
     }
 }
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/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/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/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3861739..826cd89 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -109,6 +109,13 @@
             uid -> 1000 <= uid && uid < 2000;
 
     /**
+     * Do not report any threads that have a total CPU usage (across all frequencies) less than or
+     * equal to this number. This significantly reduces the amount of reported threads without
+     * losing any important information
+     */
+    private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20;
+
+    /**
      * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
      */
     private static final int ID_ERROR = -1;
@@ -339,6 +346,15 @@
         }
         int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
 
+        // Filter threads that have low total CPU usage
+        int cpuUsageSum = 0;
+        for (int i = 0; i < cpuUsages.length; i++) {
+            cpuUsageSum += cpuUsages[i];
+        }
+        if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) {
+            return null;
+        }
+
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
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/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 2b661c2..cf2a297 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -50,6 +50,7 @@
     private int mSamplingInterval;
     private CachedDeviceState.Readonly mDeviceState;
     private long mStartTime = System.currentTimeMillis();
+    private boolean mAddDebugEntries = false;
 
     public LooperStats(int samplingInterval, int entriesSizeCap) {
         this.mSamplingInterval = samplingInterval;
@@ -60,6 +61,10 @@
         mDeviceState = deviceState;
     }
 
+    public void setAddDebugEntries(boolean addDebugEntries) {
+        mAddDebugEntries = addDebugEntries;
+    }
+
     @Override
     public Object messageDispatchStarting() {
         if (deviceStateAllowsCollection() && shouldCollectDetailedData()) {
@@ -142,9 +147,22 @@
         // Add the overflow and collision entries only if they have any data.
         maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
         maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
+        // Debug entries added to help validate the data.
+        if (mAddDebugEntries) {
+            exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
+            exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+        }
         return exportedEntries;
     }
 
+    private ExportedEntry createDebugEntry(String variableName, long value) {
+        final Entry entry = new Entry("__DEBUG_" + variableName);
+        entry.messageCount = 1;
+        entry.recordedMessageCount = 1;
+        entry.maxDelayMillis = value;
+        return new ExportedEntry(entry);
+    }
+
     /** Returns a timestamp indicating when the statistics were last reset. */
     public long getStartTimeMillis() {
         return mStartTime;
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..6fecb68 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"
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/config.xml b/core/res/res/values/config.xml
index 829d6f5..1d80961 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,7 +364,7 @@
 
     <!-- Max number of Bluetooth tethering connections allowed. If this is
          updated config_tether_dhcp_range has to be updated appropriately. -->
-    <integer translateable="false" name="config_max_pan_devices">5</integer>
+    <integer translatable="false" name="config_max_pan_devices">5</integer>
 
     <!-- Dhcp range (min, max) to use for tethering purposes -->
     <string-array translatable="false" name="config_tether_dhcp_range">
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..af291c3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2989,6 +2989,12 @@
     <!-- 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>
+
     <!-- 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 +3873,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..7a78327 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,8 @@
   <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="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
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/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/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/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b242a34..385bad5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,7 +39,9 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.List;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -56,8 +58,8 @@
             1000, 2000, 3000, 4000,
     };
     private static final int[][] THREAD_CPU_TIMES = {
-            {1, 0, 0, 1},
-            {0, 0, 0, 0},
+            {100, 0, 0, 100},
+            {0, 0, 9999999, 0},
             {1000, 1000, 1000, 1000},
             {0, 1, 2, 3},
     };
@@ -108,6 +110,42 @@
     }
 
     @Test
+    public void testReader_filtersLowTotalCpuUsage() throws IOException {
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return PROCESS_ID;
+                    }
+
+                    @Override
+                    public int myUid() {
+                        return UID;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return 0;
+                    }
+                };
+        setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME,
+                THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}});
+
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve("self/task/1/time_in_state"),
+                processUtils);
+        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+
+        List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream()
+                .map(t -> t.threadId)
+                .collect(Collectors.toList());
+        assertEquals(1, threadIds.size());
+        assertEquals(2, (long) threadIds.get(0));
+    }
+
+    @Test
     public void testReader_byUids() throws IOException {
         int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
         Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -134,7 +172,7 @@
             setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
                     new int[]{uid * 10},
                     "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
-                    new int[][]{{uid}});
+                    new int[][]{{uid + 100}});
         }
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 mProcDirectory.toPath(),
@@ -151,7 +189,7 @@
             int uid = expectedUids[i];
             checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
                     uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
-                    new int[]{1000}, new int[][]{{uid}});
+                    new int[]{1000}, new int[][]{{uid + 100}});
         }
     }
 
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/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index f637b7c..31dde5c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -105,7 +105,6 @@
         assertThat(entry.recordedDelayMessageCount).isEqualTo(1);
         assertThat(entry.delayMillis).isEqualTo(30);
         assertThat(entry.maxDelayMillis).isEqualTo(30);
-
     }
 
     @Test
@@ -429,6 +428,28 @@
         assertThat(entries).hasSize(0);
     }
 
+    @Test
+    public void testAddsDebugEntries() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        looperStats.setAddDebugEntries(true);
+
+        Message message = mHandlerFirst.obtainMessage(1000);
+        message.when = looperStats.getSystemUptimeMillis();
+        Object token = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token, message);
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(3);
+        LooperStats.ExportedEntry debugEntry1 = entries.get(1);
+        assertThat(debugEntry1.handlerClassName).isEqualTo("");
+        assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
+        assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis());
+        LooperStats.ExportedEntry debugEntry2 = entries.get(2);
+        assertThat(debugEntry2.handlerClassName).isEqualTo("");
+        assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
+        assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis());
+    }
+
     private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
         try {
             r.run();
@@ -450,6 +471,7 @@
             super(samplingInterval, sizeCap);
             this.mSamplingInterval = samplingInterval;
             this.setDeviceState(mDeviceState.getReadonlyClient());
+            this.setAddDebugEntries(false);
         }
 
         void tickRealtime(long micros) {
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 fdd638a..dea2f45 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -108,11 +108,10 @@
      * Scaled mask based on the view bounds.
      */
     private final Path mMask;
+    private final Path mMaskScaleOnly;
     private final Matrix mMaskMatrix;
     private final Region mTransparentRegion;
 
-    private Bitmap mMaskBitmap;
-
     /**
      * Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
      * background layer.
@@ -156,8 +155,8 @@
             sMask = PathParser.createPathFromPathData(
                 Resources.getSystem().getString(R.string.config_icon_mask));
         }
-        mMask = PathParser.createPathFromPathData(
-            Resources.getSystem().getString(R.string.config_icon_mask));
+        mMask = new Path(sMask);
+        mMaskScaleOnly = new Path(mMask);
         mMaskMatrix = new Matrix();
         mCanvas = new Canvas();
         mTransparentRegion = new Region();
@@ -329,24 +328,19 @@
     }
 
     private void updateMaskBoundsInternal(Rect b) {
+        // reset everything that depends on the view bounds
         mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+        sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+        mMaskMatrix.postTranslate(b.left, b.top);
         sMask.transform(mMaskMatrix, mMask);
 
-        if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() ||
-            mMaskBitmap.getHeight() != b.height()) {
-            mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+        if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+                || mLayersBitmap.getHeight() != b.height()) {
             mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
         }
-        // mMaskBitmap bound [0, w] x [0, h]
-        mCanvas.setBitmap(mMaskBitmap);
-        mPaint.setShader(null);
-        mCanvas.drawPath(mMask, mPaint);
 
-        // mMask bound [left, top, right, bottom]
-        mMaskMatrix.postTranslate(b.left, b.top);
-        mMask.reset();
-        sMask.transform(mMaskMatrix, mMask);
-        // reset everything that depends on the view bounds
+        mPaint.setShader(null);
         mTransparentRegion.setEmpty();
         mLayersShader = null;
     }
@@ -371,9 +365,11 @@
             mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
             mPaint.setShader(mLayersShader);
         }
-        if (mMaskBitmap != null) {
+        if (mMaskScaleOnly != null) {
             Rect bounds = getBounds();
-            canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint);
+            canvas.translate(bounds.left, bounds.top);
+            canvas.drawPath(mMaskScaleOnly, mPaint);
+            canvas.translate(-bounds.left, -bounds.top);
         }
     }
 
@@ -549,7 +545,7 @@
 
         final ChildDrawable[] layers = mLayerState.mChildren;
         for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
-            if (layers[i].mDrawable.isProjected()) {
+            if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) {
                 return true;
             }
         }
@@ -674,7 +670,7 @@
 
     @Override
     public int getAlpha() {
-        return PixelFormat.TRANSLUCENT;
+        return mPaint.getAlpha();
     }
 
     @Override
@@ -718,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/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1cb0d25..365be10 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -349,7 +349,7 @@
                 goto exit;
             }
         }
-        ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+        ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc,
                 targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
     }
 
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 76db18d..f7fb89e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -26,7 +26,9 @@
 
 #include <algorithm>
 #include <limits>
+#include <map>
 #include <memory>
+#include <set>
 #include <type_traits>
 
 #include <android-base/macros.h>
@@ -7033,170 +7035,178 @@
     return NO_ERROR;
 }
 
-struct IdmapTypeMap {
-    ssize_t overlayTypeId;
-    size_t entryOffset;
-    Vector<uint32_t> entryMap;
+struct IdmapMatchingResources {
+    void Add(uint32_t targetResId, uint32_t overlayResId) {
+        uint8_t targetTypeid = Res_GETTYPE(targetResId);
+        if (typeMappings.find(targetTypeid) == typeMappings.end()) {
+            typeMappings.emplace(targetTypeid, std::set<std::pair<uint32_t, uint32_t>>());
+        }
+        auto& entries = typeMappings[targetTypeid];
+        entries.insert(std::make_pair(targetResId, overlayResId));
+    }
+
+    void FixPadding() {
+        for (auto ti = typeMappings.cbegin(); ti != typeMappings.cend(); ++ti) {
+            uint32_t last_seen = 0xffffffff;
+            size_t total_entries = 0;
+            for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
+                assert(last_seen == 0xffffffff || last_seen < ei->first);
+                entryPadding[ei->first] = (last_seen == 0xffffffff) ? 0 : ei->first - last_seen - 1;
+                last_seen = ei->first;
+                total_entries += 1 + entryPadding[ei->first];
+            }
+            numberOfEntriesIncludingPadding[ti->first] = total_entries;
+        }
+    }
+
+    // resource type ID in context of target -> set of resource entries mapping target -> overlay
+    std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> typeMappings;
+
+    // resource ID in context of target -> trailing padding for that resource (call FixPadding
+    // before use)
+    std::map<uint32_t, size_t> entryPadding;
+
+    // resource type ID in context of target -> total number of entries, including padding entries,
+    // for that type (call FixPadding before use)
+    std::map<uint8_t, size_t> numberOfEntriesIncludingPadding;
 };
 
-status_t ResTable::createIdmap(const ResTable& overlay,
+status_t ResTable::createIdmap(const ResTable& targetResTable,
         uint32_t targetCrc, uint32_t overlayCrc,
         const char* targetPath, const char* overlayPath,
         void** outData, size_t* outSize) const
 {
-    // see README for details on the format of map
-    if (mPackageGroups.size() == 0) {
-        ALOGW("idmap: target package has no package groups, cannot create idmap\n");
+    if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
+        ALOGE("idmap: unexpected NULL parameter");
+        return UNKNOWN_ERROR;
+    }
+    if (strlen(targetPath) > 255) {
+        ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
+        return UNKNOWN_ERROR;
+    }
+    if (strlen(overlayPath) > 255) {
+        ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
+        return UNKNOWN_ERROR;
+    }
+    if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
+        ALOGE("idmap: invalid overlay package");
+        return UNKNOWN_ERROR;
+    }
+    if (targetResTable.mPackageGroups.size() == 0 ||
+            targetResTable.mPackageGroups[0]->packages.size() == 0) {
+        ALOGE("idmap: invalid target package");
         return UNKNOWN_ERROR;
     }
 
-    if (mPackageGroups[0]->packages.size() == 0) {
-        ALOGW("idmap: target package has no packages in its first package group, "
-                "cannot create idmap\n");
-        return UNKNOWN_ERROR;
-    }
+    const ResTable_package* targetPackageStruct = targetResTable.mPackageGroups[0]->packages[0]->package;
+    const size_t tmpNameSize = arraysize(targetPackageStruct->name);
+    char16_t tmpName[tmpNameSize];
+    strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
+    const String16 targetPackageName(tmpName);
 
-    // The number of resources overlaid that were not explicitly marked overlayable.
+    const PackageGroup* packageGroup = mPackageGroups[0];
+
+    // the number of resources overlaid that were not explicitly marked overlayable
     size_t forcedOverlayCount = 0u;
 
-    KeyedVector<uint8_t, IdmapTypeMap> map;
-
-    // overlaid packages are assumed to contain only one package group
-    const PackageGroup* pg = mPackageGroups[0];
-
-    // starting size is header
-    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
-
-    // target package id and number of types in map
-    *outSize += 2 * sizeof(uint16_t);
-
-    // overlay packages are assumed to contain only one package group
-    const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
-    char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
-    strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
-    const String16 overlayPackage(tmpName);
-
-    for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
-        const TypeList& typeList = pg->types[typeIndex];
+    // find the resources that exist in both packages
+    IdmapMatchingResources matchingResources;
+    for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
+        const TypeList& typeList = packageGroup->types[typeIndex];
         if (typeList.isEmpty()) {
             continue;
         }
-
         const Type* typeConfigs = typeList[0];
 
-        IdmapTypeMap typeMap;
-        typeMap.overlayTypeId = -1;
-        typeMap.entryOffset = 0;
-
         for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
-            uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
-            resource_name resName;
-            if (!this->getResourceName(resID, false, &resName)) {
-                if (typeMap.entryMap.isEmpty()) {
-                    typeMap.entryOffset++;
-                }
+            uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
+            resource_name current_res;
+            if (!getResourceName(overlay_resid, false, &current_res)) {
                 continue;
             }
 
             uint32_t typeSpecFlags = 0u;
-            const String16 overlayType(resName.type, resName.typeLen);
-            const String16 overlayName(resName.name, resName.nameLen);
-            uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
-                                                              overlayName.size(),
-                                                              overlayType.string(),
-                                                              overlayType.size(),
-                                                              overlayPackage.string(),
-                                                              overlayPackage.size(),
-                                                              &typeSpecFlags);
-            if (overlayResID == 0) {
-                // No such target resource was found.
-                if (typeMap.entryMap.isEmpty()) {
-                    typeMap.entryOffset++;
-                }
+            const uint32_t target_resid = targetResTable.identifierForName(
+                    current_res.name,
+                    current_res.nameLen,
+                    current_res.type,
+                    current_res.typeLen,
+                    targetPackageName.string(),
+                    targetPackageName.size(),
+                    &typeSpecFlags);
+
+            if (target_resid == 0) {
                 continue;
             }
 
-            // Now that we know this is being overlaid, check if it can be, and emit a warning if
-            // it can't.
             if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
                     ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
-                forcedOverlayCount++;
+                ++forcedOverlayCount;
             }
 
-            if (typeMap.overlayTypeId == -1) {
-                typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
-            }
-
-            if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
-                ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
-                        " but entries should map to resources of type %02zx",
-                        resID, overlayResID, typeMap.overlayTypeId);
-                return BAD_TYPE;
-            }
-
-            if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
-                // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
-                size_t index = typeMap.entryMap.size();
-                size_t numItems = entryIndex - (typeMap.entryOffset + index);
-                if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
-                    return NO_MEMORY;
-                }
-            }
-            typeMap.entryMap.add(Res_GETENTRY(overlayResID));
-        }
-
-        if (!typeMap.entryMap.isEmpty()) {
-            if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
-                return NO_MEMORY;
-            }
-            *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
+            matchingResources.Add(target_resid, overlay_resid);
         }
     }
 
-    if (map.isEmpty()) {
-        ALOGW("idmap: no resources in overlay package present in base package");
+    if (matchingResources.typeMappings.empty()) {
+        ALOGE("idmap: no matching resources");
         return UNKNOWN_ERROR;
     }
 
+    matchingResources.FixPadding();
+
+    // write idmap
+    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
+    *outSize += 2 * sizeof(uint16_t); // target package id, type count
+    const auto typesEnd = matchingResources.typeMappings.cend();
+    for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
+        *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
+        *outSize += matchingResources.numberOfEntriesIncludingPadding[ti->first] *
+            sizeof(uint32_t); // entries
+    }
     if ((*outData = malloc(*outSize)) == NULL) {
         return NO_MEMORY;
     }
 
-    uint32_t* data = (uint32_t*)*outData;
-    *data++ = htodl(IDMAP_MAGIC);
-    *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
-    *data++ = htodl(targetCrc);
-    *data++ = htodl(overlayCrc);
-    const char* paths[] = { targetPath, overlayPath };
-    for (int j = 0; j < 2; ++j) {
-        char* p = (char*)data;
-        const char* path = paths[j];
-        const size_t I = strlen(path);
-        if (I > 255) {
-            ALOGV("path exceeds expected 255 characters: %s\n", path);
-            return UNKNOWN_ERROR;
-        }
-        for (size_t i = 0; i < 256; ++i) {
-            *p++ = i < I ? path[i] : '\0';
-        }
-        data += 256 / sizeof(uint32_t);
-    }
-    const size_t mapSize = map.size();
-    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
-    *typeData++ = htods(pg->id);
-    *typeData++ = htods(mapSize);
-    for (size_t i = 0; i < mapSize; ++i) {
-        uint8_t targetTypeId = map.keyAt(i);
-        const IdmapTypeMap& typeMap = map[i];
-        *typeData++ = htods(targetTypeId + 1);
-        *typeData++ = htods(typeMap.overlayTypeId);
-        *typeData++ = htods(typeMap.entryMap.size());
-        *typeData++ = htods(typeMap.entryOffset);
+    // write idmap header
+    uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
+    *data++ = htodl(IDMAP_MAGIC); // write: magic
+    *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
+    *data++ = htodl(targetCrc); // write: target crc
+    *data++ = htodl(overlayCrc); // write: overlay crc
 
-        const size_t entryCount = typeMap.entryMap.size();
-        uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
-        for (size_t j = 0; j < entryCount; j++) {
-            entries[j] = htodl(typeMap.entryMap[j]);
+    char* charData = reinterpret_cast<char*>(data);
+    size_t pathLen = strlen(targetPath);
+    for (size_t i = 0; i < 256; ++i) {
+        *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
+    }
+    pathLen = strlen(overlayPath);
+    for (size_t i = 0; i < 256; ++i) {
+        *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
+    }
+    data += (2 * 256) / sizeof(uint32_t);
+
+    // write idmap data header
+    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+    *typeData++ = htods(targetPackageStruct->id); // write: target package id
+    *typeData++ =
+        htods(static_cast<uint16_t>(matchingResources.typeMappings.size())); // write: type count
+
+    // write idmap data
+    for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
+        const size_t entryCount = matchingResources.numberOfEntriesIncludingPadding[ti->first];
+        auto ei = ti->second.cbegin();
+        *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
+        *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
+        *typeData++ = htods(entryCount); // write: entry count
+        *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
+        uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
+        for (; ei != ti->second.cend(); ++ei) {
+            const size_t padding = matchingResources.entryPadding[ei->first];
+            for (size_t i = 0; i < padding; ++i) {
+                *entryData++ = htodl(0xffffffff); // write: padding
+            }
+            *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
         }
         typeData += entryCount * 2;
     }
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 59abad45..ad33fcf 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1990,7 +1990,7 @@
     // Return value: on success: NO_ERROR; caller is responsible for free-ing
     // outData (using free(3)). On failure, any status_t value other than
     // NO_ERROR; the caller should not free outData.
-    status_t createIdmap(const ResTable& overlay,
+    status_t createIdmap(const ResTable& targetResTable,
             uint32_t targetCrc, uint32_t overlayCrc,
             const char* targetPath, const char* overlayPath,
             void** outData, size_t* outSize) const;
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 26d2896..10b83a7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -40,7 +40,7 @@
     ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
 
     char target_name[256] = "com.android.basic";
-    ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name,
+    ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
                                                   &data_, &data_size_));
   }
 
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 60153fc..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 4a0bc9b..f68656b 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/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/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/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 0818513..450d34d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -108,13 +108,13 @@
         VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
     private static final String TAG = "NotificationEntryMgr";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    protected static final boolean ENABLE_HEADS_UP = true;
-    protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+    private static final boolean ENABLE_HEADS_UP = true;
+    private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
-    protected final NotificationMessagingUtil mMessagingUtil;
+    private final NotificationMessagingUtil mMessagingUtil;
     protected final Context mContext;
     protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
-    protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+    private final NotificationClicker mNotificationClicker = new NotificationClicker();
 
     private final NotificationGroupManager mGroupManager =
             Dependency.get(NotificationGroupManager.class);
@@ -145,14 +145,15 @@
     private NotificationPresenter mPresenter;
     private Callback mCallback;
     protected PowerManager mPowerManager;
-    protected NotificationListenerService.RankingMap mLatestRankingMap;
+    private NotificationListenerService.RankingMap mLatestRankingMap;
     protected HeadsUpManager mHeadsUpManager;
     protected NotificationData mNotificationData;
-    protected ContentObserver mHeadsUpObserver;
+    private ContentObserver mHeadsUpObserver;
     protected boolean mUseHeadsUp = false;
-    protected boolean mDisableNotificationAlerts;
+    private boolean mDisableNotificationAlerts;
     protected NotificationListContainer mListContainer;
-    protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
+    @VisibleForTesting
+    final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@@ -485,22 +486,6 @@
         updateNotifications();
     }
 
-    /**
-     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
-     * about the failure.
-     *
-     * WARNING: this will call back into us.  Don't hold any locks.
-     */
-    void handleNotificationError(StatusBarNotification n, String message) {
-        removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
-        try {
-            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
-                    n.getInitialPid(), message, n.getUserId());
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
     private void abortExistingInflation(String key) {
         if (mPendingNotifications.containsKey(key)) {
             NotificationData.Entry entry = mPendingNotifications.get(key);
@@ -513,13 +498,31 @@
         }
     }
 
+    /**
+     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+     * about the failure.
+     *
+     * WARNING: this will call back into us.  Don't hold any locks.
+     */
     @Override
-    public void handleInflationException(StatusBarNotification notification, Exception e) {
-        handleNotificationError(notification, e.getMessage());
+    public void handleInflationException(StatusBarNotification n, Exception e) {
+        removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
+        try {
+            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+                    n.getInitialPid(), e.getMessage(), n.getUserId());
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
     }
 
     private void addEntry(NotificationData.Entry shadeEntry) {
-        addNotificationViews(shadeEntry);
+        if (shadeEntry == null) {
+            return;
+        }
+        // Add the expanded view and icon.
+        mNotificationData.add(shadeEntry);
+        tagForeground(shadeEntry.notification);
+        updateNotifications();
         mCallback.onNotificationAdded(shadeEntry);
     }
 
@@ -755,17 +758,7 @@
         row.inflateViews();
     }
 
-    protected void addNotificationViews(NotificationData.Entry entry) {
-        if (entry == null) {
-            return;
-        }
-        // Add the expanded view and icon.
-        mNotificationData.add(entry);
-        tagForeground(entry.notification);
-        updateNotifications();
-    }
-
-    protected NotificationData.Entry createNotificationViews(
+    private NotificationData.Entry createNotificationViews(
             StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
             throws InflationException {
         if (DEBUG) {
@@ -841,7 +834,7 @@
     }
 
     @VisibleForTesting
-    protected void tagForeground(StatusBarNotification notification) {
+    void tagForeground(StatusBarNotification notification) {
         ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
                 notification.getUserId(), notification.getPackageName());
         if (activeOps != null) {
@@ -1098,7 +1091,7 @@
      * @param entry the entry to check
      * @return true if the entry should ambient pulse, false otherwise
      */
-    protected boolean shouldPulse(NotificationData.Entry entry) {
+    private boolean shouldPulse(NotificationData.Entry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (!getShadeController().isDozing()) {
@@ -1173,7 +1166,7 @@
         return true;
     }
 
-    protected void setNotificationShown(StatusBarNotification n) {
+    private void setNotificationShown(StatusBarNotification n) {
         setNotificationsShown(new String[]{n.getKey()});
     }
 
@@ -1185,7 +1178,7 @@
         }
     }
 
-    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+    private boolean isSnoozedPackage(StatusBarNotification sbn) {
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
 
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..daec9c9 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
@@ -3145,8 +3145,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..aa5ab71 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;
 
 /**
@@ -1287,10 +1290,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 +1320,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 +1467,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 +1483,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 +1514,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 +1920,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/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/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c0d1818..7d13679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -227,6 +227,7 @@
             mShowingSoon = false;
             if (mExpansion == EXPANSION_VISIBLE) {
                 mKeyguardView.onResume();
+                mKeyguardView.resetSecurityContainer();
             }
             StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
                 StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
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/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/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/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/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/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/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/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 8c25917..7ee3d3b 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -24,6 +24,7 @@
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.NonNull;
@@ -62,6 +63,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -73,8 +76,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import libcore.io.IoUtils;
-
 /**
  * A service to manage multiple clients that want to access the IpSec API. The service is
  * responsible for maintaining a list of clients and managing the resources (and related quotas)
@@ -621,7 +622,8 @@
                                 mConfig.getDestinationAddress(),
                                 spi,
                                 mConfig.getMarkValue(),
-                                mConfig.getMarkMask());
+                                mConfig.getMarkMask(),
+                                mConfig.getXfrmInterfaceId());
             } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
             }
@@ -683,7 +685,8 @@
                     mSrvConfig
                             .getNetdInstance()
                             .ipSecDeleteSecurityAssociation(
-                                    uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+                                    uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+                                    0 /* mask */, 0 /* if_id */);
                 }
             } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -795,6 +798,8 @@
         private final int mIkey;
         private final int mOkey;
 
+        private final int mIfId;
+
         TunnelInterfaceRecord(
                 int resourceId,
                 String interfaceName,
@@ -802,7 +807,8 @@
                 String localAddr,
                 String remoteAddr,
                 int ikey,
-                int okey) {
+                int okey,
+                int intfId) {
             super(resourceId);
 
             mInterfaceName = interfaceName;
@@ -811,6 +817,7 @@
             mRemoteAddress = remoteAddr;
             mIkey = ikey;
             mOkey = okey;
+            mIfId = intfId;
         }
 
         /** always guarded by IpSecService#this */
@@ -821,7 +828,7 @@
             //       Delete global policies
             try {
                 final INetd netd = mSrvConfig.getNetdInstance();
-                netd.removeVirtualTunnelInterface(mInterfaceName);
+                netd.ipSecRemoveTunnelInterface(mInterfaceName);
 
                 for (int selAddrFamily : ADDRESS_FAMILIES) {
                     netd.ipSecDeleteSecurityPolicy(
@@ -829,13 +836,15 @@
                             selAddrFamily,
                             IpSecManager.DIRECTION_OUT,
                             mOkey,
-                            0xffffffff);
+                            0xffffffff,
+                            mIfId);
                     netd.ipSecDeleteSecurityPolicy(
                             uid,
                             selAddrFamily,
                             IpSecManager.DIRECTION_IN,
                             mIkey,
-                            0xffffffff);
+                            0xffffffff,
+                            mIfId);
                 }
             } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(
@@ -877,6 +886,10 @@
             return mOkey;
         }
 
+        public int getIfId() {
+            return mIfId;
+        }
+
         @Override
         protected ResourceTracker getResourceTracker() {
             return getUserRecord().mTunnelQuotaTracker;
@@ -1286,7 +1299,7 @@
             //       Add inbound/outbound global policies
             //              (use reqid = 0)
             final INetd netd = mSrvConfig.getNetdInstance();
-            netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
+            netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
 
             for (int selAddrFamily : ADDRESS_FAMILIES) {
                 // Always send down correct local/remote addresses for template.
@@ -1298,7 +1311,8 @@
                         remoteAddr,
                         0,
                         okey,
-                        0xffffffff);
+                        0xffffffff,
+                        resourceId);
                 netd.ipSecAddSecurityPolicy(
                         callerUid,
                         selAddrFamily,
@@ -1307,7 +1321,8 @@
                         localAddr,
                         0,
                         ikey,
-                        0xffffffff);
+                        0xffffffff,
+                        resourceId);
             }
 
             userRecord.mTunnelInterfaceRecords.put(
@@ -1320,7 +1335,8 @@
                                     localAddr,
                                     remoteAddr,
                                     ikey,
-                                    okey),
+                                    okey,
+                                    resourceId),
                             binder));
             return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
         } catch (RemoteException e) {
@@ -1523,6 +1539,9 @@
                 throw new IllegalArgumentException(
                         "Invalid IpSecTransform.mode: " + config.getMode());
         }
+
+        config.setMarkValue(0);
+        config.setMarkMask(0);
     }
 
     private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
@@ -1584,7 +1603,8 @@
                         (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
                         encapType,
                         encapLocalPort,
-                        encapRemotePort);
+                        encapRemotePort,
+                        c.getXfrmInterfaceId());
     }
 
     /**
@@ -1740,27 +1760,48 @@
                         : tunnelInterfaceInfo.getIkey();
 
         try {
-            c.setMarkValue(mark);
-            c.setMarkMask(0xffffffff);
+            // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
+            // SPI matching as part of the template resolution.
+            int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
+            c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
+
+            // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
+            //     (and backporting) would allow us to narrow the mark space, and ensure that the SA
+            //     and SPs have matching marks (as VTI are meant to be built).
+            // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
+            //     config matches the actual allocated resources in the kernel.
+            // All SAs will have zero marks (from creation time), and any policy that matches the
+            //     same src/dst could match these SAs. Non-IpSecService governed processes that
+            //     establish floating policies with the same src/dst may result in undefined
+            //     behavior. This is generally limited to vendor code due to the permissions
+            //     (CAP_NET_ADMIN) required.
+            //
+            // c.setMarkValue(mark);
+            // c.setMarkMask(0xffffffff);
 
             if (direction == IpSecManager.DIRECTION_OUT) {
                 // Set output mark via underlying network (output only)
                 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
 
-                // If outbound, also add SPI to the policy.
-                for (int selAddrFamily : ADDRESS_FAMILIES) {
-                    mSrvConfig
-                            .getNetdInstance()
-                            .ipSecUpdateSecurityPolicy(
-                                    callingUid,
-                                    selAddrFamily,
-                                    direction,
-                                    tunnelInterfaceInfo.getLocalAddress(),
-                                    tunnelInterfaceInfo.getRemoteAddress(),
-                                    transformInfo.getSpiRecord().getSpi(),
-                                    mark,
-                                    0xffffffff);
-                }
+                // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
+                // but want to guarantee outbound packets are sent over the new SA.
+                spi = transformInfo.getSpiRecord().getSpi();
+            }
+
+            // Always update the policy with the relevant XFRM_IF_ID
+            for (int selAddrFamily : ADDRESS_FAMILIES) {
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecUpdateSecurityPolicy(
+                                callingUid,
+                                selAddrFamily,
+                                direction,
+                                transformInfo.getConfig().getSourceAddress(),
+                                transformInfo.getConfig().getDestinationAddress(),
+                                spi, // If outbound, also add SPI to the policy.
+                                mark, // Must always set policy mark; ikey/okey for VTIs
+                                0xffffffff,
+                                c.getXfrmInterfaceId());
             }
 
             // Update SA with tunnel mark (ikey or okey based on direction)
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/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 2dee3a0..c563ad2 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -141,6 +141,7 @@
         if (mEnabled != enabled) {
             mEnabled = enabled;
             mStats.reset();
+            mStats.setAddDebugEntries(enabled);
             Looper.setObserver(enabled ? mStats : null);
         }
     }
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/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a8f7259..3c14393 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -23,24 +23,18 @@
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
 import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
 import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
-import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
-import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
-import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
-import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
 import static android.net.ConnectivityManager.TETHERING_INVALID;
 import static android.net.ConnectivityManager.TETHERING_USB;
 import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -50,6 +44,7 @@
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+
 import static com.android.server.ConnectivityService.SHORT_ARG;
 
 import android.app.Notification;
@@ -60,7 +55,6 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfile.ServiceListener;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -68,7 +62,6 @@
 import android.hardware.usb.UsbManager;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
-import android.net.ip.IpServer;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -76,7 +69,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
-import android.net.RouteInfo;
+import android.net.ip.IpServer;
 import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
@@ -89,15 +82,12 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
-import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -113,6 +103,7 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.LocalServices;
+import com.android.server.connectivity.tethering.EntitlementManager;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadController;
 import com.android.server.connectivity.tethering.TetheringConfiguration;
@@ -123,8 +114,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -145,18 +134,12 @@
     private final static boolean DBG = false;
     private final static boolean VDBG = false;
 
-    protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
-
     private static final Class[] messageClasses = {
             Tethering.class, TetherMasterSM.class, IpServer.class
     };
     private static final SparseArray<String> sMagicDecoderRing =
             MessageUtils.findMessageNames(messageClasses);
 
-    // {@link ComponentName} of the Service used to run tether provisioning.
-    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
-            .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
-
     private static class TetherState {
         public final IpServer ipServer;
         public int lastState;
@@ -191,7 +174,6 @@
     private final INetworkStatsService mStatsService;
     private final INetworkPolicyManager mPolicyManager;
     private final Looper mLooper;
-    private final MockableSystemProperties mSystemProperties;
     private final StateMachine mTetherMasterSM;
     private final OffloadController mOffloadController;
     private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@@ -200,6 +182,7 @@
     private final HashSet<IpServer> mForwardedDownstreams;
     private final VersionedBroadcastListener mCarrierConfigChange;
     private final TetheringDependencies mDeps;
+    private final EntitlementManager mEntitlementMgr;
 
     private volatile TetheringConfiguration mConfig;
     private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -220,7 +203,6 @@
         mStatsService = statsService;
         mPolicyManager = policyManager;
         mLooper = looper;
-        mSystemProperties = systemProperties;
         mDeps = deps;
 
         mPublicSync = new Object();
@@ -241,12 +223,13 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+        mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
         mCarrierConfigChange = new VersionedBroadcastListener(
                 "CarrierConfigChangeListener", mContext, smHandler, filter,
                 (Intent ignored) -> {
                     mLog.log("OBSERVED carrier config change");
                     updateConfiguration();
-                    reevaluateSimCardProvisioning();
+                    mEntitlementMgr.reevaluateSimCardProvisioning();
                 });
 
         mStateReceiver = new StateReceiver();
@@ -289,6 +272,7 @@
     private void updateConfiguration() {
         mConfig = new TetheringConfiguration(mContext, mLog);
         mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
+        mEntitlementMgr.updateConfiguration(mConfig);
     }
 
     private void maybeUpdateConfiguration() {
@@ -354,83 +338,54 @@
     }
 
     public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
-        if (!isTetherProvisioningRequired()) {
+        mEntitlementMgr.startTethering(type);
+        if (!mEntitlementMgr.isTetherProvisioningRequired()) {
             enableTetheringInternal(type, true, receiver);
             return;
         }
 
+        final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
         if (showProvisioningUi) {
-            runUiTetherProvisioningAndEnable(type, receiver);
+            mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver);
         } else {
-            runSilentTetherProvisioningAndEnable(type, receiver);
+            mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver);
         }
     }
 
     public void stopTethering(int type) {
         enableTetheringInternal(type, false, null);
-        if (isTetherProvisioningRequired()) {
-            cancelTetherProvisioningRechecks(type);
+        mEntitlementMgr.stopTethering(type);
+        if (mEntitlementMgr.isTetherProvisioningRequired()) {
+            // There are lurking bugs where the notion of "provisioning required" or
+            // "tethering supported" may change without notifying tethering properly, then
+            // tethering can't shutdown correctly.
+            // TODO: cancel re-check all the time
+            if (mDeps.isTetheringSupported()) {
+                mEntitlementMgr.cancelTetherProvisioningRechecks(type);
+            }
         }
     }
 
     /**
-     * Check if the device requires a provisioning check in order to enable tethering.
-     *
-     * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
-     */
-    @VisibleForTesting
-    protected boolean isTetherProvisioningRequired() {
-        final TetheringConfiguration cfg = mConfig;
-        if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
-                || cfg.provisioningApp.length == 0) {
-            return false;
-        }
-        if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
-            return false;
-        }
-        return (cfg.provisioningApp.length == 2);
-    }
-
-    // The logic here is aimed solely at confirming that a CarrierConfig exists
-    // and affirms that entitlement checks are not required.
-    //
-    // TODO: find a better way to express this, or alter the checking process
-    // entirely so that this is more intuitive.
-    private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
-        // Check carrier config for entitlement checks
-        final CarrierConfigManager configManager = (CarrierConfigManager) mContext
-             .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager == null) return false;
-
-        final PersistableBundle carrierConfig = configManager.getConfig();
-        if (carrierConfig == null) return false;
-
-        // A CarrierConfigManager was found and it has a config.
-        final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
-        return !isEntitlementCheckRequired;
-    }
-
-    /**
      * Enables or disables tethering for the given type. This should only be called once
      * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
      * for the specified interface.
      */
     private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
-        boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
+        boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired();
         int result;
         switch (type) {
             case TETHERING_WIFI:
                 result = setWifiTethering(enable);
                 if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
-                    scheduleProvisioningRechecks(type);
+                    mEntitlementMgr.scheduleProvisioningRechecks(type);
                 }
                 sendTetherResult(receiver, result);
                 break;
             case TETHERING_USB:
                 result = setUsbTethering(enable);
                 if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
-                    scheduleProvisioningRechecks(type);
+                    mEntitlementMgr.scheduleProvisioningRechecks(type);
                 }
                 sendTetherResult(receiver, result);
                 break;
@@ -489,32 +444,14 @@
                         ? TETHER_ERROR_NO_ERROR
                         : TETHER_ERROR_MASTER_ERROR;
                 sendTetherResult(receiver, result);
-                if (enable && isTetherProvisioningRequired()) {
-                    scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
+                if (enable && mEntitlementMgr.isTetherProvisioningRequired()) {
+                    mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
                 }
                 adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
             }
         }, BluetoothProfile.PAN);
     }
 
-    private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
-        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
-        sendUiTetherProvisionIntent(type, proxyReceiver);
-    }
-
-    private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
-        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
-        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     /**
      * Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
      * is successful before firing back up to the wrapped receiver.
@@ -546,62 +483,6 @@
         return receiverForSending;
     }
 
-    private void scheduleProvisioningRechecks(int type) {
-        Intent intent = new Intent();
-        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_SET_ALARM, true);
-        intent.setComponent(TETHER_SERVICE);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
-        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
-        sendSilentTetherProvisionIntent(type, proxyReceiver);
-    }
-
-    private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
-        Intent intent = new Intent();
-        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_RUN_PROVISION, true);
-        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
-        intent.setComponent(TETHER_SERVICE);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void cancelTetherProvisioningRechecks(int type) {
-        if (mDeps.isTetheringSupported()) {
-            Intent intent = new Intent();
-            intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
-            intent.setComponent(TETHER_SERVICE);
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    // Used by the SIM card change observation code.
-    // TODO: De-duplicate with above code, where possible.
-    private void startProvisionIntent(int tetherType) {
-        final Intent startProvIntent = new Intent();
-        startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
-        startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
-        startProvIntent.setComponent(TETHER_SERVICE);
-        mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
-    }
-
     public int tether(String iface) {
         return tether(iface, IpServer.STATE_TETHERED);
     }
@@ -1166,30 +1047,6 @@
         return false;
     }
 
-    private void reevaluateSimCardProvisioning() {
-        if (!mConfig.hasMobileHotspotProvisionApp()) return;
-        if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
-
-        ArrayList<Integer> tethered = new ArrayList<>();
-        synchronized (mPublicSync) {
-            for (int i = 0; i < mTetherStates.size(); i++) {
-                TetherState tetherState = mTetherStates.valueAt(i);
-                if (tetherState.lastState != IpServer.STATE_TETHERED) {
-                    continue;  // Skip interfaces that aren't tethered.
-                }
-                String iface = mTetherStates.keyAt(i);
-                int interfaceType = ifaceNameToType(iface);
-                if (interfaceType != TETHERING_INVALID) {
-                    tethered.add(interfaceType);
-                }
-            }
-        }
-
-        for (int tetherType : tethered) {
-            startProvisionIntent(tetherType);
-        }
-    }
-
     class TetherMasterSM extends StateMachine {
         private static final int BASE_MASTER                    = Protocol.BASE_TETHERING;
         // an interface SM has requested Tethering/Local Hotspot
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
new file mode 100644
index 0000000..a4e3e1d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -0,0 +1,224 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+
+import static com.android.internal.R.string.config_wifi_tether_enable;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.Binder;
+import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.MockableSystemProperties;
+
+/**
+ * This class encapsulates entitlement/provisioning mechanics
+ * provisioning check only applies to the use of the mobile network as an upstream
+ *
+ * @hide
+ */
+public class EntitlementManager {
+    private static final String TAG = EntitlementManager.class.getSimpleName();
+
+    // {@link ComponentName} of the Service used to run tether provisioning.
+    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
+            Resources.getSystem().getString(config_wifi_tether_enable));
+    protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
+    // The ArraySet contains enabled downstream types, ex:
+    // {@link ConnectivityManager.TETHERING_WIFI}
+    // {@link ConnectivityManager.TETHERING_USB}
+    // {@link ConnectivityManager.TETHERING_BLUETOOTH}
+    @GuardedBy("mCurrentTethers")
+    private final ArraySet<Integer> mCurrentTethers;
+    private final Context mContext;
+    private final MockableSystemProperties mSystemProperties;
+    private final SharedLog mLog;
+    @Nullable
+    private TetheringConfiguration mConfig;
+
+    public EntitlementManager(Context ctx, SharedLog log,
+            MockableSystemProperties systemProperties) {
+        mContext = ctx;
+        mLog = log;
+        mCurrentTethers = new ArraySet<Integer>();
+        mSystemProperties = systemProperties;
+    }
+
+    /**
+     * Pass a new TetheringConfiguration instance each time when
+     * Tethering#updateConfiguration() is called.
+     */
+    public void updateConfiguration(TetheringConfiguration conf) {
+        mConfig = conf;
+    }
+
+    /**
+     * Tell EntitlementManager that a given type of tethering has been enabled
+     *
+     * @param type Tethering type
+     */
+    public void startTethering(int type) {
+        synchronized (mCurrentTethers) {
+            mCurrentTethers.add(type);
+        }
+    }
+
+    /**
+     * Tell EntitlementManager that a given type of tethering has been disabled
+     *
+     * @param type Tethering type
+     */
+    public void stopTethering(int type) {
+        synchronized (mCurrentTethers) {
+            mCurrentTethers.remove(type);
+        }
+    }
+
+    /**
+     * Check if the device requires a provisioning check in order to enable tethering.
+     *
+     * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+     */
+    @VisibleForTesting
+    public boolean isTetherProvisioningRequired() {
+        if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
+                || mConfig.provisioningApp.length == 0) {
+            return false;
+        }
+        if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
+            return false;
+        }
+        return (mConfig.provisioningApp.length == 2);
+    }
+
+    /**
+     * Re-check tethering provisioning for enabled downstream tether types.
+     * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+     */
+    public void reevaluateSimCardProvisioning() {
+        if (!mConfig.hasMobileHotspotProvisionApp()) return;
+        if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
+
+        final ArraySet<Integer> reevaluateType;
+        synchronized (mCurrentTethers) {
+            reevaluateType = new ArraySet<Integer>(mCurrentTethers);
+        }
+        for (Integer type : reevaluateType) {
+            startProvisionIntent(type);
+        }
+    }
+
+    // The logic here is aimed solely at confirming that a CarrierConfig exists
+    // and affirms that entitlement checks are not required.
+    //
+    // TODO: find a better way to express this, or alter the checking process
+    // entirely so that this is more intuitive.
+    private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
+        // Check carrier config for entitlement checks
+        final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) return false;
+
+        final PersistableBundle carrierConfig = configManager.getConfig();
+        if (carrierConfig == null) return false;
+
+        // A CarrierConfigManager was found and it has a config.
+        final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+        return !isEntitlementCheckRequired;
+    }
+
+    public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_RUN_PROVISION, true);
+        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Used by the SIM card change observation code.
+    // TODO: De-duplicate with above code, where possible.
+    private void startProvisionIntent(int tetherType) {
+        final Intent startProvIntent = new Intent();
+        startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+        startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+        startProvIntent.setComponent(TETHER_SERVICE);
+        mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+    }
+
+    public void scheduleProvisioningRechecks(int type) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_SET_ALARM, true);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void cancelTetherProvisioningRechecks(int type) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 8b40069..d56b167 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -17,19 +17,13 @@
 package com.android.server.connectivity.tethering;
 
 import android.content.Context;
-import android.net.INetd;
 import android.net.NetworkRequest;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
 import android.net.ip.IpServer;
-import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.os.Handler;
 import android.net.util.SharedLog;
-import android.os.Looper;
+import android.os.Handler;
 
 import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.MockableSystemProperties;
 
 import java.util.ArrayList;
 
@@ -65,4 +59,9 @@
     public NetworkRequest getDefaultNetworkRequest() {
         return null;
     }
+
+    public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
+            MockableSystemProperties systemProperties) {
+        return new EntitlementManager(ctx, log, systemProperties);
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e70460a..d04fa23 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -247,9 +247,6 @@
     // device).
     private Point mStableDisplaySize = new Point();
 
-    // Whether the system has finished booting or not.
-    private boolean mSystemReady;
-
     // The top inset of the default display.
     // This gets persisted so that the boot animation knows how to transition from the display's
     // full size to the size configured by the user. Right now we only persist and animate the top
@@ -322,8 +319,6 @@
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
         mCurrentUserId = UserHandle.USER_SYSTEM;
-
-        mSystemReady = false;
     }
 
     public void setupSchedulerPolicies() {
@@ -413,10 +408,6 @@
         synchronized (mSyncRoot) {
             mSafeMode = safeMode;
             mOnlyCore = onlyCore;
-            mSystemReady = true;
-            // Just in case the top inset changed before the system was ready. At this point, any
-            // relevant configuration should be in place.
-            recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
         }
 
         mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -1065,10 +1056,7 @@
     }
 
     private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
-        // We must only persist the inset after boot has completed, otherwise we will end up
-        // overwriting the persisted value before the masking flag has been loaded from the
-        // resource overlay.
-        if (!mSystemReady || d == null) {
+        if (d == null) {
             return;
         }
         int topInset = d.getInsets().top;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6f726e6..9566598 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -57,8 +57,6 @@
  * </p>
  */
 final class LogicalDisplay {
-    private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";
-
     private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
 
     // The layer stack we use when the display has been blanked to prevent any
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/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1b03d1..d471904 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -22,14 +22,11 @@
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityThread;
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -37,7 +34,6 @@
 import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
 import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
@@ -59,11 +55,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
-import com.android.internal.util.ConcurrentUtils;
 import com.android.server.FgThread;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.UserManagerService;
@@ -84,8 +78,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -228,8 +220,6 @@
 
     private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
 
-    private Future<?> mInitCompleteSignal;
-
     public OverlayManagerService(@NonNull final Context context,
             @NonNull final Installer installer) {
         super(context);
@@ -241,29 +231,28 @@
         mSettings = new OverlayManagerSettings();
         mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
                 getDefaultOverlayPackages(), new OverlayChangeListener());
-        mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
-            final IntentFilter packageFilter = new IntentFilter();
-            packageFilter.addAction(ACTION_PACKAGE_ADDED);
-            packageFilter.addAction(ACTION_PACKAGE_CHANGED);
-            packageFilter.addAction(ACTION_PACKAGE_REMOVED);
-            packageFilter.addDataScheme("package");
-            getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
-                    packageFilter, null, null);
 
-            final IntentFilter userFilter = new IntentFilter();
-            userFilter.addAction(ACTION_USER_ADDED);
-            userFilter.addAction(ACTION_USER_REMOVED);
-            getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
-                    userFilter, null, null);
+        final IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme("package");
+        getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+                packageFilter, null, null);
 
-            restoreSettings();
+        final IntentFilter userFilter = new IntentFilter();
+        userFilter.addAction(ACTION_USER_ADDED);
+        userFilter.addAction(ACTION_USER_REMOVED);
+        getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
+                userFilter, null, null);
 
-            initIfNeeded();
-            onSwitchUser(UserHandle.USER_SYSTEM);
+        restoreSettings();
 
-            publishBinderService(Context.OVERLAY_SERVICE, mService);
-            publishLocalService(OverlayManagerService.class, this);
-        }, "Init OverlayManagerService");
+        initIfNeeded();
+        onSwitchUser(UserHandle.USER_SYSTEM);
+
+        publishBinderService(Context.OVERLAY_SERVICE, mService);
+        publishLocalService(OverlayManagerService.class, this);
     }
 
     @Override
@@ -271,32 +260,6 @@
         // Intentionally left empty.
     }
 
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) {
-            ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
-                    "Wait for OverlayManagerService init");
-            mInitCompleteSignal = null;
-        }
-    }
-
-    public void updateSystemUiContext() {
-        if (mInitCompleteSignal != null) {
-            ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
-                    "Wait for OverlayManagerService init");
-            mInitCompleteSignal = null;
-        }
-
-        final ApplicationInfo ai;
-        try {
-            ai = mPackageManager.mPackageManager.getApplicationInfo("android",
-                    GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-        ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
-    }
-
     private void initIfNeeded() {
         final UserManager um = getContext().getSystemService(UserManager.class);
         final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
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/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..14b2f01c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1039,11 +1039,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 +2426,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 +2446,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 +2564,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/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/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
new file mode 100644
index 0000000..05912a5
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -0,0 +1,268 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager.InstallUpdateCallback;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.UpdateEngine;
+import android.os.UpdateEngineCallback;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+/**
+ * Used for installing an update on <a href="https://source.android.com/devices/tech/ota/ab">AB
+ * devices.</a>
+ * <p>This logic is specific to GOTA and should be modified by OEMs using a different AB update
+ * system.</p>
+ */
+class AbUpdateInstaller extends UpdateInstaller {
+    private static final String PAYLOAD_BIN = "payload.bin";
+    private static final String PAYLOAD_PROPERTIES_TXT = "payload_properties.txt";
+    //https://en.wikipedia.org/wiki/Zip_(file_format)#Local_file_header
+    private static final int OFFSET_TO_FILE_NAME = 30;
+    // kDownloadStateInitializationError constant from system/update_engine/common/error_code.h.
+    private static final int DOWNLOAD_STATE_INITIALIZATION_ERROR = 20;
+    private long mSizeForUpdate;
+    private long mOffsetForUpdate;
+    private List<String> mProperties;
+    private Enumeration<? extends ZipEntry> mEntries;
+    private ZipFile mPackedUpdateFile;
+    private static final Map<Integer, Integer> errorCodesMap = buildErrorCodesMap();
+    private static final Map<Integer, String> errorStringsMap = buildErrorStringsMap();
+    public static final String UNKNOWN_ERROR = "Unknown error with error code = ";
+    private boolean mUpdateInstalled;
+
+    private static Map<Integer, Integer> buildErrorCodesMap() {
+        Map<Integer, Integer> map = new HashMap<>();
+        map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                DOWNLOAD_STATE_INITIALIZATION_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+
+        // Error constants corresponding to errors related to bad update file.
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+
+        // Error constants corresponding to errors related to devices bad state.
+        map.put(
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+
+        return map;
+    }
+
+    private static Map<Integer, String> buildErrorStringsMap() {
+        Map<Integer, String> map = new HashMap<>();
+        map.put(UpdateEngine.ErrorCodeConstants.ERROR, UNKNOWN_ERROR);
+        map.put(
+                DOWNLOAD_STATE_INITIALIZATION_ERROR,
+                "The delta update payload was targeted for another version or the source partition"
+                        + "was modified after it was installed");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                "Failed to finish the configured postinstall works.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                "Failed to open one of the partitions it tried to write to or read data from.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                "Payload mismatch error.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                "Failed to read the payload data from the given URL.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, "Payload hash error.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                "Payload size mismatch error.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                "Failed to verify the signature of the payload.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                "The payload has been successfully installed,"
+                        + "but the active slot was not flipped.");
+        return map;
+    }
+
+    AbUpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+            StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+            DevicePolicyConstants constants) {
+        super(context, updateFileDescriptor, callback, injector, constants);
+        mUpdateInstalled = false;
+    }
+
+    @Override
+    public void installUpdateInThread() {
+        if (mUpdateInstalled) {
+            throw new IllegalStateException("installUpdateInThread can be called only once.");
+        }
+        try {
+            setState();
+            applyPayload(Paths.get(mCopiedUpdateFile.getAbsolutePath()).toUri().toString());
+        } catch (ZipException e) {
+            Log.w(UpdateInstaller.TAG, e);
+            notifyCallbackOnError(
+                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    Log.getStackTraceString(e));
+        } catch (IOException e) {
+            Log.w(UpdateInstaller.TAG, e);
+            notifyCallbackOnError(
+                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e));
+        }
+    }
+
+    private void setState() throws IOException {
+        mUpdateInstalled = true;
+        mPackedUpdateFile = new ZipFile(mCopiedUpdateFile);
+        mProperties = new ArrayList<>();
+        mSizeForUpdate = -1;
+        mOffsetForUpdate = 0;
+        mEntries = mPackedUpdateFile.entries();
+    }
+
+    private UpdateEngine buildBoundUpdateEngine() {
+        UpdateEngine updateEngine = new UpdateEngine();
+        updateEngine.bind(new DelegatingUpdateEngineCallback(this, updateEngine));
+        return updateEngine;
+    }
+
+    private void applyPayload(String updatePath) throws IOException {
+        if (!updateStateForPayload()) {
+            return;
+        }
+        String[] headerKeyValuePairs = mProperties.stream().toArray(String[]::new);
+        if (mSizeForUpdate == -1) {
+            Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package.");
+            notifyCallbackOnError(
+                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    "Failed to find payload entry in the given package.");
+            return;
+        }
+
+        UpdateEngine updateEngine = buildBoundUpdateEngine();
+        updateEngine.applyPayload(
+                updatePath, mOffsetForUpdate, mSizeForUpdate, headerKeyValuePairs);
+    }
+
+    private boolean updateStateForPayload() throws IOException {
+        long offset = 0;
+        while (mEntries.hasMoreElements()) {
+            ZipEntry entry = mEntries.nextElement();
+
+            String name = entry.getName();
+            offset += buildOffsetForEntry(entry, name);
+            if (entry.isDirectory()) {
+                offset -= entry.getCompressedSize();
+                continue;
+            }
+            if (PAYLOAD_BIN.equals(name)) {
+                if (entry.getMethod() != ZipEntry.STORED) {
+                    Log.w(UpdateInstaller.TAG, "Invalid compression method.");
+                    notifyCallbackOnError(
+                            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                            "Invalid compression method.");
+                    return false;
+                }
+                mSizeForUpdate = entry.getCompressedSize();
+                mOffsetForUpdate = offset - entry.getCompressedSize();
+            } else if (PAYLOAD_PROPERTIES_TXT.equals(name)) {
+                updatePropertiesForEntry(entry);
+            }
+        }
+        return true;
+    }
+
+    private long buildOffsetForEntry(ZipEntry entry, String name) {
+        return OFFSET_TO_FILE_NAME + name.length() + entry.getCompressedSize()
+                + (entry.getExtra() == null ? 0 : entry.getExtra().length);
+    }
+
+    private void updatePropertiesForEntry(ZipEntry entry) throws IOException {
+        try (BufferedReader bufferedReader = new BufferedReader(
+                new InputStreamReader(mPackedUpdateFile.getInputStream(entry)))) {
+            String line;
+            /* Neither @line nor @mProperties are size constraint since there is a few properties
+            with limited size. */
+            while ((line = bufferedReader.readLine()) != null) {
+                mProperties.add(line);
+            }
+        }
+    }
+
+    private static class DelegatingUpdateEngineCallback extends UpdateEngineCallback {
+        private UpdateInstaller mUpdateInstaller;
+        private UpdateEngine mUpdateEngine;
+
+        DelegatingUpdateEngineCallback(
+                UpdateInstaller updateInstaller, UpdateEngine updateEngine) {
+            mUpdateInstaller = updateInstaller;
+            mUpdateEngine = updateEngine;
+        }
+
+        @Override
+        public void onStatusUpdate(int statusCode, float percentage) {
+            return;
+        }
+
+        @Override
+        public void onPayloadApplicationComplete(int errorCode) {
+            mUpdateEngine.unbind();
+            if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) {
+                mUpdateInstaller.notifyCallbackOnSuccess();
+            } else {
+                mUpdateInstaller.notifyCallbackOnError(
+                        errorCodesMap.getOrDefault(
+                                errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN),
+                        errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode));
+            }
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 5926bdd..6462d16 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,7 +17,9 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
 import android.content.ComponentName;
+import android.os.ParcelFileDescriptor;
 
 import com.android.server.SystemService;
 
@@ -91,4 +93,8 @@
 
     @Override
     public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
+
+    @Override
+    public void installUpdateFromFile(ComponentName admin,
+            ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 71fea02..fd59b43 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -42,6 +42,12 @@
     private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
             "das_died_service_stable_connection_threshold_sec";
 
+    private static final String BATTERY_THRESHOLD_NOT_CHARGING_KEY =
+            "battery_threshold_not_charging";
+
+    private static final String BATTERY_THRESHOLD_CHARGING_KEY =
+            "battery_threshold_charging";
+
     /**
      * The back-off before re-connecting, when a service binding died, due to the owner
      * crashing repeatedly.
@@ -63,6 +69,17 @@
      */
     public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
 
+    /**
+     * Battery threshold for installing system update while the device is not charging.
+     */
+    public final int BATTERY_THRESHOLD_NOT_CHARGING;
+
+    /**
+     * Battery threshold for installing system update while the device is charging.
+     */
+    public final int BATTERY_THRESHOLD_CHARGING;
+
+
     private DevicePolicyConstants(String settings) {
 
         final KeyValueListParser parser = new KeyValueListParser(',');
@@ -87,6 +104,12 @@
                 DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
                 TimeUnit.MINUTES.toSeconds(2));
 
+        int batteryThresholdNotCharging = parser.getInt(
+                BATTERY_THRESHOLD_NOT_CHARGING_KEY, 40);
+
+        int batteryThresholdCharging = parser.getInt(
+                BATTERY_THRESHOLD_CHARGING_KEY, 20);
+
         // Set minimum: 5 seconds.
         dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
 
@@ -103,6 +126,8 @@
         DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
         DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
                 dasDiedServiceStableConnectionThresholdSec;
+        BATTERY_THRESHOLD_NOT_CHARGING = batteryThresholdNotCharging;
+        BATTERY_THRESHOLD_CHARGING = batteryThresholdCharging;
     }
 
     public static DevicePolicyConstants loadFromString(String settings) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bbbc40c..7751b4a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -113,6 +113,7 @@
 import android.app.admin.PasswordMetrics;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
@@ -379,6 +380,8 @@
     private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
     private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
     private static final Set<Integer> DA_DISALLOWED_POLICIES;
+    private static final String AB_DEVICE_KEY = "ro.build.ab_update";
+
     static {
         SECURE_SETTINGS_WHITELIST = new ArraySet<>();
         SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -13315,4 +13318,30 @@
 
         return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
     }
+
+    @Override
+    public void installUpdateFromFile(ComponentName admin,
+            ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
+        enforceDeviceOwner(admin);
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            UpdateInstaller updateInstaller;
+            if (isDeviceAB()) {
+                updateInstaller = new AbUpdateInstaller(
+                        mContext, updateFileDescriptor, callback, mInjector, mConstants);
+            } else {
+                updateInstaller = new NonAbUpdateInstaller(
+                        mContext, updateFileDescriptor, callback, mInjector, mConstants);
+            }
+            updateInstaller.startInstallUpdate();
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+
+    private boolean isDeviceAB() {
+        return "true".equalsIgnoreCase(android.os.SystemProperties
+                .get(AB_DEVICE_KEY, ""));
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
new file mode 100644
index 0000000..5f1e926
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
@@ -0,0 +1,52 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.RecoverySystem;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Used for installing an update for <a href="https://source.android.com/devices/tech/ota/nonab">non
+ * AB</a> devices.
+ */
+class NonAbUpdateInstaller extends UpdateInstaller {
+    NonAbUpdateInstaller(Context context,
+            ParcelFileDescriptor updateFileDescriptor,
+            StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+            DevicePolicyConstants constants) {
+        super(context, updateFileDescriptor, callback, injector, constants);
+    }
+
+    @Override
+    public void installUpdateInThread() {
+        try {
+            RecoverySystem.installPackage(mContext, mCopiedUpdateFile);
+            notifyCallbackOnSuccess();
+        } catch (IOException e) {
+            Log.w(TAG, "IO error while trying to install non AB update.", e);
+            notifyCallbackOnError(
+                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    Log.getStackTraceString(e));
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
new file mode 100644
index 0000000..7910598
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -0,0 +1,146 @@
+/*
+ * 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.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+abstract class UpdateInstaller {
+    private StartInstallingUpdateCallback mCallback;
+    private ParcelFileDescriptor mUpdateFileDescriptor;
+    private DevicePolicyConstants mConstants;
+    protected Context mContext;
+    protected File mCopiedUpdateFile;
+
+    static final String TAG = "UpdateInstaller";
+    private DevicePolicyManagerService.Injector mInjector;
+
+    protected UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+            StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+            DevicePolicyConstants constants) {
+        mContext = context;
+        mCallback = callback;
+        mUpdateFileDescriptor = updateFileDescriptor;
+        mInjector = injector;
+        mConstants = constants;
+    }
+
+    public abstract void installUpdateInThread();
+
+    public void startInstallUpdate() {
+        if (!checkIfBatteryIsSufficient()) {
+            notifyCallbackOnError(
+                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
+                    "The battery level must be above "
+                            + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
+                            + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
+            return;
+        }
+        Thread thread = new Thread(() -> {
+            mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir();
+            if (mCopiedUpdateFile == null) {
+                notifyCallbackOnError(
+                        DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                        "Error while copying file.");
+                return;
+            }
+            installUpdateInThread();
+        });
+        thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+    }
+
+    private boolean checkIfBatteryIsSufficient() {
+        BatteryManager batteryManager =
+                (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
+        if (batteryManager != null) {
+            int chargePercentage = batteryManager
+                    .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+            return batteryManager.isCharging()
+                    ? chargePercentage >= mConstants.BATTERY_THRESHOLD_CHARGING
+                    : chargePercentage >= mConstants.BATTERY_THRESHOLD_NOT_CHARGING;
+        }
+        return false;
+    }
+
+    private File copyUpdateFileToDataOtaPackageDir() {
+        try {
+            File destination = createNewFileWithPermissions();
+            copyToFile(destination);
+            return destination;
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to copy update file to OTA directory", e);
+            notifyCallbackOnError(
+                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    Log.getStackTraceString(e));
+            return null;
+        }
+    }
+
+    private File createNewFileWithPermissions() throws IOException {
+        File destination = File.createTempFile(
+                "update", ".zip", new File(Environment.getDataDirectory() + "/ota_package"));
+        FileUtils.setPermissions(
+                /* path= */ destination,
+                /* mode= */ FileUtils.S_IRWXU | FileUtils.S_IRGRP | FileUtils.S_IROTH,
+                /* uid= */ -1, /* gid= */ -1);
+        return destination;
+    }
+
+    private void copyToFile(File destination) throws IOException {
+        try (OutputStream out = new FileOutputStream(destination);
+             InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+                     mUpdateFileDescriptor)) {
+            FileUtils.copy(in, out);
+        }
+    }
+
+    void cleanupUpdateFile() {
+        if (mCopiedUpdateFile.exists()) {
+            mCopiedUpdateFile.delete();
+        }
+    }
+
+    protected void notifyCallbackOnError(int errorCode, String errorMessage) {
+        cleanupUpdateFile();
+        try {
+            mCallback.onStartInstallingUpdateError(errorCode, errorMessage);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while calling callback", e);
+        }
+    }
+
+    protected void notifyCallbackOnSuccess() {
+        cleanupUpdateFile();
+        mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
+    }
+}
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8ac41f..56f7cff 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
 import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.DisplayManagerInternal;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -717,17 +716,9 @@
 
         // Manages Overlay packages
         traceBeginAndSlog("StartOverlayManagerService");
-        OverlayManagerService overlayManagerService = new OverlayManagerService(
-                mSystemContext, installer);
-        mSystemServiceManager.startService(overlayManagerService);
+        mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
         traceEnd();
 
-        if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
-            // DisplayManager needs the overlay immediately.
-            overlayManagerService.updateSystemUiContext();
-            LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
-        }
-
         // The sensor service needs access to package manager service, app ops
         // service, and permissions service, therefore we start it after them.
         // Start sensor service in a separate thread. Completion should be checked
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 565152c..6f10ed5 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,6 +26,7 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    bmgrlib \
     services.backup \
     services.core \
     services.net
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
new file mode 100644
index 0000000..1705f5b
--- /dev/null
+++ b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.commands.bmgr;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/** Unit tests for {@link com.android.commands.bmgr.Bmgr}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BmgrTest {
+    @Mock private IBackupManager mBackupManager;
+    private Bmgr mBmgr;
+
+    /** Common setup run before each test method. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mBmgr = new Bmgr(mBackupManager);
+    }
+
+    /**
+     * Test that bmgr uses the default user {@link UserHandle.USER_SYSTEM} if no user is specified.
+     */
+    @Test
+    public void testRun_whenUserNotSpecified_callsBackupManagerAsSystemUser() throws Exception {
+        mBmgr.run(new String[] {"run"});
+
+        verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM);
+    }
+
+    /** Test that bmgr uses the specified user if an user is specified. */
+    @Test
+    public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception {
+        mBmgr.run(new String[] {"--user", "10"});
+
+        verify(mBackupManager).isBackupServiceActive(10);
+    }
+}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
new file mode 100644
index 0000000..ebc816d
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+android_test {
+    name: "FrameworksMockingServicesTests",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "services.core",
+        "services.net",
+        "androidx.test.runner",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
deleted file mode 100644
index b21b3e4..0000000
--- a/services/tests/mockingservicestests/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    services.core \
-    services.net \
-    androidx.test.runner \
-    mockito-target-extended-minus-junit4 \
-    platform-test-annotations \
-
-LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
-
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libstaticjvmtiagent \
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
new file mode 100644
index 0000000..e804342
--- /dev/null
+++ b/services/tests/servicestests/Android.bp
@@ -0,0 +1,104 @@
+//########################################################################
+// Build FrameworksServicesTests package
+//########################################################################
+
+android_test {
+    name: "FrameworksServicesTests",
+
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+
+        "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl",
+        "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl",
+
+        "test-apps/JobTestApp/src/**/*.java",
+
+        "test-apps/SuspendTestApp/src/**/*.java",
+    ],
+    static_libs: [
+        "frameworks-base-testutils",
+        "services.accessibility",
+        "services.appwidget",
+        "services.autofill",
+        "services.backup",
+        "services.core",
+        "services.devicepolicy",
+        "services.net",
+        "services.usage",
+        "guava",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
+        "ShortcutManagerTestUtils",
+        "truth-prebuilt",
+        "testables",
+        "testng",
+        "ub-uiautomator",
+        "platformprotosnano",
+        "hamcrest-library",
+        "servicestests-utils",
+    ],
+
+    aidl: {
+        local_include_dirs: ["aidl"],
+    },
+
+    libs: [
+        "android.hidl.manager-V1.0-java",
+        "android.hardware.tv.cec-V1.0-java",
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    // These are not normally accessible from apps so they must be explicitly included.
+    jni_libs: [
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbinderthreadstate",
+        "libc++",
+        "libcutils",
+        "liblog",
+        "liblzma",
+        "libnativehelper",
+        "libnetdaidl",
+        "libui",
+        "libunwind",
+        "libutils",
+    ],
+
+    dxflags: ["--multi-dex"],
+
+    optimize: {
+        enabled: false,
+    },
+}
+
+java_library {
+    name: "servicestests-utils",
+    srcs: [
+        "utils/**/*.java",
+    ],
+    static_libs: [
+        "android-support-test",
+        "mockito-target-minus-junit4",
+    ],
+    libs: [
+        "android.test.runner",
+    ],
+}
+
+filegroup {
+    name: "servicestests-SuspendTestApp-files",
+    srcs: [
+        "src/com/android/server/pm/SuspendPackagesTest.java",
+    ],
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
deleted file mode 100644
index e2f8995..0000000
--- a/services/tests/servicestests/Android.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-#########################################################################
-# Build FrameworksServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
-    services.accessibility \
-    services.appwidget \
-    services.autofill \
-    services.backup \
-    services.core \
-    services.devicepolicy \
-    services.net \
-    services.usage \
-    guava \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-target-minus-junit4 \
-    platform-test-annotations \
-    ShortcutManagerTestUtils \
-    truth-prebuilt \
-    testables \
-    testng \
-    ub-uiautomator\
-    platformprotosnano \
-    hamcrest-library
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
-    aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
-
-LOCAL_JAVA_LIBRARIES := \
-    android.hidl.manager-V1.0-java \
-    android.hardware.tv.cec-V1.0-java \
-    android.test.mock \
-    android.test.base android.test.runner \
-
-LOCAL_PACKAGE_NAME := FrameworksServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libbacktrace \
-    libbase \
-    libbinder \
-    libbinderthreadstate \
-    libc++ \
-    libcutils \
-    liblog \
-    liblzma \
-    libnativehelper \
-    libnetdaidl \
-    libui \
-    libunwind \
-    libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
new file mode 100644
index 0000000..d4e53dd
--- /dev/null
+++ b/services/tests/servicestests/aidl/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+    name: "servicestests-aidl",
+    sdk_version: "current",
+    srcs: [
+        "com/android/servicestests/aidl/INetworkStateObserver.aidl",
+        "com/android/servicestests/aidl/ICmdReceiverService.aidl",
+    ],
+}
diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk
deleted file mode 100644
index 166da1d..0000000
--- a/services/tests/servicestests/aidl/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
-        com/android/servicestests/aidl/INetworkStateObserver.aidl \
-        com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_MODULE := servicestests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/services/tests/servicestests/test-apps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
new file mode 100644
index 0000000..13e6644
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "ConnTestApp",
+
+    test_suites: ["device-tests"],
+
+    static_libs: ["servicestests-aidl"],
+    srcs: ["**/*.java"],
+
+    platform_apis: true,
+    certificate: "platform",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
deleted file mode 100644
index 18b8c2d..0000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := ConnTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
new file mode 100644
index 0000000..ae1eca7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "JobTestApp",
+
+    sdk_version: "current",
+
+    test_suites: ["device-tests"],
+
+    srcs: ["**/*.java"],
+
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
deleted file mode 100644
index 7893c91..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := JobTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
new file mode 100644
index 0000000..7257275
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+android_test_helper_app {
+    name: "SuspendTestApp",
+
+    test_suites: ["device-tests"],
+
+    static_libs: [
+        "androidx.test.runner",
+        "ub-uiautomator",
+    ],
+
+    srcs: [
+        "**/*.java",
+        ":servicestests-SuspendTestApp-files",
+    ],
+
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+
+    platform_apis: true,
+
+}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
deleted file mode 100644
index ab222b9..0000000
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
-
-LOCAL_PACKAGE_NAME := SuspendTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
new file mode 100644
index 0000000..c2cb6881
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+    name: "ShortcutManagerTestUtils",
+
+    srcs: ["src/**/*.java"],
+
+    libs: [
+        "mockito-target",
+        "android.test.runner.stubs",
+    ],
+
+    sdk_version: "test_current",
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
deleted file mode 100644
index 019bcbd3..0000000
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
-    mockito-target \
-    android.test.runner.stubs
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ShortcutManagerTestUtils
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
new file mode 100644
index 0000000..ca8cc0d
--- /dev/null
+++ b/services/tests/uiservicestests/Android.bp
@@ -0,0 +1,58 @@
+//########################################################################
+// Build FrameworksUiServicesTests package
+//########################################################################
+
+android_test {
+    name: "FrameworksUiServicesTests",
+
+    // Include test java files
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "services.accessibility",
+        "services.core",
+        "services.devicepolicy",
+        "services.net",
+        "services.usage",
+        "guava",
+        "android-support-test",
+        "mockito-target-inline-minus-junit4",
+        "platform-test-annotations",
+        "testables",
+    ],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    dxflags: ["--multi-dex"],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    compile_multilib: "both",
+
+    // These are not normally accessible from apps so they must be explicitly included.
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libmultiplejvmtiagentsinterferenceagent",
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbinderthreadstate",
+        "libc++",
+        "libcutils",
+        "liblog",
+        "liblzma",
+        "libnativehelper",
+        "libnetdaidl",
+        "libui",
+        "libunwindstack",
+        "libutils",
+    ],
+}
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
deleted file mode 100644
index f3f4355..0000000
--- a/services/tests/uiservicestests/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-#########################################################################
-# Build FrameworksUiServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include test java files and source from notifications package.
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-	$(call all-java-files-under, ../../core/java/com/android/server/notification) \
-	$(call all-java-files-under, ../../core/java/com/android/server/slice) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    services.accessibility \
-    services.core \
-    services.devicepolicy \
-    services.net \
-    services.usage \
-    guava \
-    android-support-test \
-    mockito-target-inline-minus-junit4 \
-    platform-test-annotations \
-    testables
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MULTILIB := both
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libmultiplejvmtiagentsinterferenceagent \
-    libbacktrace \
-    libbase \
-    libbinder \
-    libbinderthreadstate \
-    libc++ \
-    libcutils \
-    liblog \
-    liblzma \
-    libnativehelper \
-    libnetdaidl \
-    libui \
-    libunwindstack \
-    libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_PACKAGE)
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/Android.bp b/services/tests/wmtests/Android.bp
new file mode 100644
index 0000000..cdba9a1
--- /dev/null
+++ b/services/tests/wmtests/Android.bp
@@ -0,0 +1,52 @@
+//########################################################################
+// Build WmTests package
+//########################################################################
+
+android_test {
+    name: "WmTests",
+
+    // We only want this apk build for tests.
+
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "frameworks-base-testutils",
+        "services.core",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "servicestests-utils",
+        "truth-prebuilt",
+        "testables",
+        "ub-uiautomator",
+        "hamcrest-library",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    // These are not normally accessible from apps so they must be explicitly included.
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    dxflags: ["--multi-dex"],
+
+    optimize: {
+        enabled: false,
+    },
+
+}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
deleted file mode 100644
index 67c2860..0000000
--- a/services/tests/wmtests/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-#########################################################################
-# Build WmTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, ../servicestests/utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
-    services.core \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-target-extended-minus-junit4 \
-    platform-test-annotations \
-    truth-prebuilt \
-    testables \
-    ub-uiautomator \
-    hamcrest-library
-
-LOCAL_JAVA_LIBRARIES := \
-    android.test.mock \
-    android.test.base \
-    android.test.runner \
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libstaticjvmtiagent \
-
-LOCAL_PACKAGE_NAME := WmTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
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/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 771faaf..be1a455 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -47,6 +47,7 @@
         assertNull(c.getEncryption());
         assertNull(c.getAuthentication());
         assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
+        assertEquals(0, c.getXfrmInterfaceId());
     }
 
     private IpSecConfig getSampleConfig() {
@@ -77,6 +78,7 @@
         c.setNattKeepaliveInterval(42);
         c.setMarkValue(12);
         c.setMarkMask(23);
+        c.setXfrmInterfaceId(34);
 
         return c;
     }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9b919abf..4dc0341 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -71,6 +71,9 @@
     private final LinkAddress mLocalInnerAddress;
     private final int mFamily;
 
+    private static final int[] ADDRESS_FAMILIES =
+            new int[] {AF_INET, AF_INET6};
+
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
         return Arrays.asList(
@@ -196,6 +199,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -231,6 +235,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -304,7 +309,8 @@
                         eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
                         eq(config.getEncapType()),
                         eq(encapSocketPort),
-                        eq(config.getEncapRemotePort()));
+                        eq(config.getEncapRemotePort()),
+                        eq(config.getXfrmInterfaceId()));
     }
 
     @Test
@@ -430,6 +436,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
         // quota is not released until the SPI is released by the Transform
         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -452,6 +459,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -469,6 +477,7 @@
                         anyString(),
                         anyInt(),
                         anyInt(),
+                        anyInt(),
                         anyInt());
         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
 
@@ -504,6 +513,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -572,11 +582,12 @@
 
         assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
         verify(mMockNetd)
-                .addVirtualTunnelInterface(
+                .ipSecAddTunnelInterface(
                         eq(createTunnelResp.interfaceName),
                         eq(mSourceAddr),
                         eq(mDestinationAddr),
                         anyInt(),
+                        anyInt(),
                         anyInt());
     }
 
@@ -591,7 +602,7 @@
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
-        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
@@ -614,7 +625,7 @@
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
-        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
@@ -624,6 +635,41 @@
     }
 
     @Test
+    public void testApplyTunnelModeTransform() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+        int transformResourceId = createTransformResp.resourceId;
+        int tunnelResourceId = createTunnelResp.resourceId;
+        mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+                transformResourceId, "blessedPackage");
+
+        for (int selAddrFamily : ADDRESS_FAMILIES) {
+            verify(mMockNetd)
+                    .ipSecUpdateSecurityPolicy(
+                            eq(mUid),
+                            eq(selAddrFamily),
+                            eq(IpSecManager.DIRECTION_OUT),
+                            anyString(),
+                            anyString(),
+                            eq(TEST_SPI),
+                            anyInt(), // iKey/oKey
+                            anyInt(), // mask
+                            eq(tunnelResourceId));
+        }
+
+        ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+    @Test
     public void testAddRemoveAddressFromTunnelInterface() throws Exception {
         for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
             IpSecTunnelInterfaceResponse createTunnelResp =
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/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a6ed9f2..8081812 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -23,15 +23,15 @@
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
 import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
 import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
 
@@ -39,19 +39,18 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -91,9 +90,9 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
-import android.os.test.TestLooper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -126,7 +125,6 @@
 public class TetheringTest {
     private static final int IFINDEX_OFFSET = 100;
 
-    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
     private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
     private static final String TEST_USB_IFNAME = "test_rndis0";
@@ -370,61 +368,6 @@
         mServiceContext.unregisterReceiver(mBroadcastReceiver);
     }
 
-    private void setupForRequiredProvisioning() {
-        // Produce some acceptable looking provision app setting if requested.
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(PROVISIONING_APP_NAME);
-        // Don't disable tethering provisioning unless requested.
-        when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
-                                          anyBoolean())).thenReturn(false);
-        // Act like the CarrierConfigManager is present and ready unless told otherwise.
-        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
-                .thenReturn(mCarrierConfigManager);
-        when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
-        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
-    }
-
-    @Test
-    public void canRequireProvisioning() {
-        setupForRequiredProvisioning();
-        sendConfigurationChanged();
-        assertTrue(mTethering.isTetherProvisioningRequired());
-    }
-
-    @Test
-    public void toleratesCarrierConfigManagerMissing() {
-        setupForRequiredProvisioning();
-        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
-                .thenReturn(null);
-        sendConfigurationChanged();
-        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
-        // We therefore still require provisioning.
-        assertTrue(mTethering.isTetherProvisioningRequired());
-    }
-
-    @Test
-    public void toleratesCarrierConfigMissing() {
-        setupForRequiredProvisioning();
-        when(mCarrierConfigManager.getConfig()).thenReturn(null);
-        sendConfigurationChanged();
-        // We still have a provisioning app configured, so still require provisioning.
-        assertTrue(mTethering.isTetherProvisioningRequired());
-    }
-
-    @Test
-    public void provisioningNotRequiredWhenAppNotFound() {
-        setupForRequiredProvisioning();
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(null);
-        assertTrue(!mTethering.isTetherProvisioningRequired());
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(new String[] {"malformedApp"});
-        assertTrue(!mTethering.isTetherProvisioningRequired());
-    }
-
     private void sendWifiApStateChanged(int state) {
         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         intent.putExtra(EXTRA_WIFI_AP_STATE, state);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
new file mode 100644
index 0000000..0f72229
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import com.android.internal.R;
+import com.android.server.connectivity.MockableSystemProperties;
+
+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;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class EntitlementManagerTest {
+
+    private static final int EVENT_EM_UPDATE = 1;
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+
+    @Mock private CarrierConfigManager mCarrierConfigManager;
+    @Mock private Context mContext;
+    @Mock private ContentResolver mContent;
+    @Mock private MockableSystemProperties mSystemProperties;
+    @Mock private Resources mResources;
+    @Mock private SharedLog mLog;
+
+    // Like so many Android system APIs, these cannot be mocked because it is marked final.
+    // We have to use the real versions.
+    private final PersistableBundle mCarrierConfig = new PersistableBundle();
+
+    private EntitlementManager mEnMgr;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getContentResolver()).thenReturn(mContent);
+        when(mResources.getStringArray(R.array.config_tether_dhcp_range))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getIntArray(R.array.config_tether_upstream_types))
+            .thenReturn(new int[0]);
+        when(mLog.forSubComponent(anyString())).thenReturn(mLog);
+
+        mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+    }
+
+    @After
+    public void tearDown() throws Exception {}
+
+    private void setupForRequiredProvisioning() {
+        // Produce some acceptable looking provision app setting if requested.
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(PROVISIONING_APP_NAME);
+        // Don't disable tethering provisioning unless requested.
+        when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
+                anyBoolean())).thenReturn(false);
+        // Act like the CarrierConfigManager is present and ready unless told otherwise.
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+    }
+
+    @Test
+    public void canRequireProvisioning() {
+        setupForRequiredProvisioning();
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        assertTrue(mEnMgr.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void toleratesCarrierConfigManagerMissing() {
+        setupForRequiredProvisioning();
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+            .thenReturn(null);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+        // Therefore provisioning still be required.
+        assertTrue(mEnMgr.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void toleratesCarrierConfigMissing() {
+        setupForRequiredProvisioning();
+        when(mCarrierConfigManager.getConfig()).thenReturn(null);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        // We still have a provisioning app configured, so still require provisioning.
+        assertTrue(mEnMgr.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void provisioningNotRequiredWhenAppNotFound() {
+        setupForRequiredProvisioning();
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+            .thenReturn(null);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        assertFalse(mEnMgr.isTetherProvisioningRequired());
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+            .thenReturn(new String[] {"malformedApp"});
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        assertFalse(mEnMgr.isTetherProvisioningRequired());
+    }
+
+}
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index 03c0b9a..afe91cd 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -145,6 +145,13 @@
             if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
                 continue
 
+            misspelled_attributes = [
+              ("translateable", "translatable"),
+            ]
+            for misspelling, expected in misspelled_attributes:
+                if misspelling in child.attrib:
+                    error(child, "Misspelled <string> attribute.", misspelling, expected)
+
             limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
             if limit is None:
                 info(child, "Missing CHAR LIMIT to aid translation",
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/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 25dcdd8..893b19c 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -49,7 +49,7 @@
     /**
      * SSID of the network to connect for service sign-up.
      */
-    private final WifiSsid mOsuSsid;
+    private WifiSsid mOsuSsid;
 
     /**
      * Friendly name of the OSU provider.
@@ -130,6 +130,10 @@
         return mOsuSsid;
     }
 
+    public void setOsuSsid(WifiSsid osuSsid) {
+        mOsuSsid = osuSsid;
+    }
+
     public String getFriendlyName() {
         return mFriendlyName;
     }
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 5c9db53..a62d63c 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -150,6 +150,12 @@
     public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22;
 
     /**
+     * The reason code for provisioning failure when an {@link OsuProvider} is not found for
+     * provisioning.
+     */
+    public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23;
+
+    /**
      * The status code for provisioning flow to indicate connecting to OSU AP
      */
     public static final int OSU_STATUS_AP_CONNECTING = 1;
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();
+    }
 }