Merge "Deactivate bundled opportunistic subscription."
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45884c4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/.idea
+*.iml
diff --git a/Android.bp b/Android.bp
index cc9cfe9..34162d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -162,6 +162,7 @@
         "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
+        "core/java/android/hardware/display/IColorDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
         "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
@@ -653,6 +654,8 @@
         "core/java/com/android/server/DropboxLogTags.logtags",
         "core/java/org/chromium/arc/EventLogTags.logtags",
 
+        ":platform-properties",
+
         ":framework-statslog-gen",
     ],
 
@@ -729,6 +732,7 @@
         "android.hardware.radio-V1.4-java",
         "android.hardware.usb.gadget-V1.0-java",
         "netd_aidl_interface-java",
+        "devicepolicyprotosnano",
     ],
 
     required: [
diff --git a/Android.mk b/Android.mk
index b7dda9a..92e33e9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,10 +76,9 @@
 .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
 $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
     frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
-    frameworks/base/config/hiddenapi-light-greylist.txt \
-    frameworks/base/config/hiddenapi-vendor-list.txt \
+    frameworks/base/config/hiddenapi-greylist.txt \
+    frameworks/base/config/hiddenapi-greylist-max-p.txt \
     frameworks/base/config/hiddenapi-greylist-max-o.txt \
-    frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
     frameworks/base/config/hiddenapi-force-blacklist.txt \
     $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
     $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
@@ -88,10 +87,9 @@
 	    --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
 	    --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
 	    --csv $(PRIVATE_FLAGS_INPUTS) \
-	    --greylist \
-	        frameworks/base/config/hiddenapi-light-greylist.txt \
-	        frameworks/base/config/hiddenapi-vendor-list.txt \
+	    --greylist frameworks/base/config/hiddenapi-greylist.txt \
 	    --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+	    --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
 	    --greylist-max-o-ignore-conflicts \
 	        frameworks/base/config/hiddenapi-greylist-max-o.txt \
 	    --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
diff --git a/api/current.txt b/api/current.txt
index 83827ea..802e50e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7692,6 +7692,7 @@
     method protected void prepareView(android.view.View);
     method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
     method public void setExecutor(java.util.concurrent.Executor);
+    method public void setOnLightBackground(boolean);
     method public void updateAppWidget(android.widget.RemoteViews);
     method public void updateAppWidgetOptions(android.os.Bundle);
     method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -16498,6 +16499,7 @@
     method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
+    method public boolean isSessionConfigurationSupported(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
     field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -17095,8 +17097,9 @@
     field public static final int RED = 0; // 0x0
   }
 
-  public final class SessionConfiguration {
+  public final class SessionConfiguration implements android.os.Parcelable {
     ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback);
+    method public int describeContents();
     method public java.util.concurrent.Executor getExecutor();
     method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
     method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
@@ -17105,6 +17108,8 @@
     method public android.hardware.camera2.CameraCaptureSession.StateCallback getStateCallback();
     method public void setInputConfiguration(android.hardware.camera2.params.InputConfiguration);
     method public void setSessionParameters(android.hardware.camera2.CaptureRequest);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.SessionConfiguration> CREATOR;
     field public static final int SESSION_HIGH_SPEED = 1; // 0x1
     field public static final int SESSION_REGULAR = 0; // 0x0
   }
@@ -23436,6 +23441,17 @@
     method public void onTearDown(android.media.AudioTrack);
   }
 
+  public class CallbackDataSourceDesc extends android.media.DataSourceDesc {
+    method public android.media.DataSourceCallback getDataSourceCallback();
+  }
+
+  public static class CallbackDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase {
+    ctor public CallbackDataSourceDesc.Builder();
+    ctor public CallbackDataSourceDesc.Builder(android.media.CallbackDataSourceDesc);
+    method public android.media.CallbackDataSourceDesc build();
+    method public android.media.CallbackDataSourceDesc.Builder setDataSource(android.media.DataSourceCallback);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -23488,6 +23504,26 @@
     field public static final int QUALITY_MEDIUM = 1; // 0x1
   }
 
+  public abstract class DataSourceCallback implements java.io.Closeable {
+    ctor public DataSourceCallback();
+    method public abstract long getSize() throws java.io.IOException;
+    method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
+  }
+
+  public class DataSourceDesc {
+    method public long getEndPosition();
+    method public java.lang.String getMediaId();
+    method public long getStartPosition();
+    field public static final long LONG_MAX_TIME_MS = 576460752303423L; // 0x20c49ba5e353fL
+    field public static final long POSITION_UNKNOWN = 576460752303423L; // 0x20c49ba5e353fL
+  }
+
+  protected static class DataSourceDesc.BuilderBase<T extends android.media.DataSourceDesc.BuilderBase> {
+    method public T setEndPosition(long);
+    method public T setMediaId(java.lang.String);
+    method public T setStartPosition(long);
+  }
+
   public final class DeniedByServerException extends android.media.MediaDrmException {
     ctor public DeniedByServerException(java.lang.String);
   }
@@ -23688,6 +23724,21 @@
     field public static final int EULER_Z = 2; // 0x2
   }
 
+  public class FileDataSourceDesc extends android.media.DataSourceDesc {
+    method public long getLength();
+    method public long getOffset();
+    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+    field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class FileDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase {
+    ctor public FileDataSourceDesc.Builder();
+    ctor public FileDataSourceDesc.Builder(android.media.FileDataSourceDesc);
+    method public android.media.FileDataSourceDesc build();
+    method public android.media.FileDataSourceDesc.Builder setDataSource(android.os.ParcelFileDescriptor);
+    method public android.media.FileDataSourceDesc.Builder setDataSource(android.os.ParcelFileDescriptor, long, long);
+  }
+
   public abstract class Image implements java.lang.AutoCloseable {
     method public abstract void close();
     method public android.graphics.Rect getCropRect();
@@ -24551,16 +24602,30 @@
 
   public final class MediaFormat {
     ctor public MediaFormat();
+    ctor public MediaFormat(android.media.MediaFormat);
+    method public boolean containsFeature(java.lang.String);
     method public boolean containsKey(java.lang.String);
     method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
     method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
     method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
     method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
+    method public java.nio.ByteBuffer getByteBuffer(java.lang.String, java.nio.ByteBuffer);
     method public boolean getFeatureEnabled(java.lang.String);
+    method public java.util.Set<java.lang.String> getFeatures();
     method public float getFloat(java.lang.String);
+    method public float getFloat(java.lang.String, float);
     method public int getInteger(java.lang.String);
+    method public int getInteger(java.lang.String, int);
+    method public java.util.Set<java.lang.String> getKeys();
     method public long getLong(java.lang.String);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.Number getNumber(java.lang.String);
+    method public java.lang.Number getNumber(java.lang.String, java.lang.Number);
     method public java.lang.String getString(java.lang.String);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public int getValueTypeForKey(java.lang.String);
+    method public void removeFeature(java.lang.String);
+    method public void removeKey(java.lang.String);
     method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
     method public void setFeatureEnabled(java.lang.String, boolean);
     method public void setFloat(java.lang.String, float);
@@ -24665,6 +24730,12 @@
     field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
     field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
     field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+    field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
+    field public static final int TYPE_FLOAT = 3; // 0x3
+    field public static final int TYPE_INTEGER = 1; // 0x1
+    field public static final int TYPE_LONG = 2; // 0x2
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_STRING = 4; // 0x4
   }
 
   public final class MediaMetadata implements android.os.Parcelable {
@@ -25055,6 +25126,166 @@
     field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
   }
 
+  public class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable {
+    ctor public MediaPlayer2(android.content.Context);
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public java.lang.Object attachAuxEffect(int);
+    method public boolean cancelCommand(java.lang.Object);
+    method public java.lang.Object clearNextDataSources();
+    method public void clearPendingCommands();
+    method public void close();
+    method public java.lang.Object deselectTrack(int);
+    method public android.media.AudioAttributes getAudioAttributes();
+    method public int getAudioSessionId();
+    method public long getBufferedPosition();
+    method public android.media.DataSourceDesc getCurrentDataSource();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public float getMaxPlayerVolume();
+    method public android.os.PersistableBundle getMetrics();
+    method public android.media.PlaybackParams getPlaybackParams();
+    method public float getPlayerVolume();
+    method public android.media.AudioDeviceInfo getPreferredDevice();
+    method public android.media.AudioDeviceInfo getRoutedDevice();
+    method public int getSelectedTrack(int);
+    method public int getState();
+    method public android.media.SyncParams getSyncParams();
+    method public android.media.MediaTimestamp getTimestamp();
+    method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
+    method public android.media.VideoSize getVideoSize();
+    method public boolean isLooping();
+    method public java.lang.Object loopCurrent(boolean);
+    method public java.lang.Object notifyWhenCommandLabelReached(java.lang.Object);
+    method public java.lang.Object pause();
+    method public java.lang.Object play();
+    method public java.lang.Object prepare();
+    method public void registerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.EventCallback);
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public void reset();
+    method public java.lang.Object seekTo(long);
+    method public java.lang.Object seekTo(long, int);
+    method public java.lang.Object selectTrack(int);
+    method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
+    method public java.lang.Object setAudioSessionId(int);
+    method public java.lang.Object setAuxEffectSendLevel(float);
+    method public java.lang.Object setDataSource(android.media.DataSourceDesc);
+    method public java.lang.Object setDisplay(android.view.SurfaceHolder);
+    method public java.lang.Object setNextDataSource(android.media.DataSourceDesc);
+    method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>);
+    method public java.lang.Object setPlaybackParams(android.media.PlaybackParams);
+    method public java.lang.Object setPlayerVolume(float);
+    method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
+    method public java.lang.Object setScreenOnWhilePlaying(boolean);
+    method public java.lang.Object setSurface(android.view.Surface);
+    method public java.lang.Object setSyncParams(android.media.SyncParams);
+    method public java.lang.Object setWakeLock(android.os.PowerManager.WakeLock);
+    method public java.lang.Object skipToNext();
+    method public void unregisterEventCallback(android.media.MediaPlayer2.EventCallback);
+    field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
+    field public static final int CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES = 30; // 0x1e
+    field public static final int CALL_COMPLETED_DESELECT_TRACK = 2; // 0x2
+    field public static final int CALL_COMPLETED_LOOP_CURRENT = 3; // 0x3
+    field public static final int CALL_COMPLETED_PAUSE = 4; // 0x4
+    field public static final int CALL_COMPLETED_PLAY = 5; // 0x5
+    field public static final int CALL_COMPLETED_PREPARE = 6; // 0x6
+    field public static final int CALL_COMPLETED_SEEK_TO = 14; // 0xe
+    field public static final int CALL_COMPLETED_SELECT_TRACK = 15; // 0xf
+    field public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16; // 0x10
+    field public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17; // 0x11
+    field public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
+    field public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19; // 0x13
+    field public static final int CALL_COMPLETED_SET_DISPLAY = 33; // 0x21
+    field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22; // 0x16
+    field public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23; // 0x17
+    field public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24; // 0x18
+    field public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26; // 0x1a
+    field public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35; // 0x23
+    field public static final int CALL_COMPLETED_SET_SURFACE = 27; // 0x1b
+    field public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28; // 0x1c
+    field public static final int CALL_COMPLETED_SET_WAKE_LOCK = 34; // 0x22
+    field public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29; // 0x1d
+    field public static final int CALL_STATUS_BAD_VALUE = 2; // 0x2
+    field public static final int CALL_STATUS_ERROR_IO = 4; // 0x4
+    field public static final int CALL_STATUS_ERROR_UNKNOWN = -2147483648; // 0x80000000
+    field public static final int CALL_STATUS_INVALID_OPERATION = 1; // 0x1
+    field public static final int CALL_STATUS_NO_DRM_SCHEME = 6; // 0x6
+    field public static final int CALL_STATUS_NO_ERROR = 0; // 0x0
+    field public static final int CALL_STATUS_PERMISSION_DENIED = 3; // 0x3
+    field public static final int CALL_STATUS_SKIPPED = 5; // 0x5
+    field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
+    field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
+    field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
+    field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
+    field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
+    field public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4; // 0x4
+    field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
+    field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
+    field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
+    field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
+    field public static final int MEDIA_INFO_DATA_SOURCE_END = 5; // 0x5
+    field public static final int MEDIA_INFO_DATA_SOURCE_LIST_END = 6; // 0x6
+    field public static final int MEDIA_INFO_DATA_SOURCE_REPEAT = 7; // 0x7
+    field public static final int MEDIA_INFO_DATA_SOURCE_START = 2; // 0x2
+    field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
+    field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+    field public static final int MEDIA_INFO_PREPARED = 100; // 0x64
+    field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
+    field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
+    field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
+    field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
+    field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
+    field public static final int PLAYER_STATE_ERROR = 1005; // 0x3ed
+    field public static final int PLAYER_STATE_IDLE = 1001; // 0x3e9
+    field public static final int PLAYER_STATE_PAUSED = 1003; // 0x3eb
+    field public static final int PLAYER_STATE_PLAYING = 1004; // 0x3ec
+    field public static final int PLAYER_STATE_PREPARED = 1002; // 0x3ea
+    field public static final int SEEK_CLOSEST = 3; // 0x3
+    field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
+    field public static final int SEEK_NEXT_SYNC = 1; // 0x1
+    field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
+  }
+
+  public static class MediaPlayer2.EventCallback {
+    ctor public MediaPlayer2.EventCallback();
+    method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+    method public void onCommandLabelReached(android.media.MediaPlayer2, java.lang.Object);
+    method public void onError(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+    method public void onInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+    method public void onMediaTimeDiscontinuity(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaTimestamp);
+    method public void onSubtitleData(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.SubtitleData);
+    method public void onTimedMetaDataAvailable(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.TimedMetaData);
+    method public void onVideoSizeChanged(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.VideoSize);
+  }
+
+  public static final class MediaPlayer2.MetricsConstants {
+    field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+    field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+    field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
+    field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
+    field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
+    field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
+    field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+    field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
+    field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
+    field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
+    field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
+    field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
+  }
+
+  public static class MediaPlayer2.TrackInfo {
+    method public android.media.MediaFormat getFormat();
+    method public java.lang.String getLanguage();
+    method public int getTrackType();
+    field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
+    field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
+    field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
+    field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
+  }
+
   public class MediaRecorder implements android.media.AudioRouting {
     ctor public MediaRecorder();
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -25797,6 +26028,26 @@
     ctor public UnsupportedSchemeException(java.lang.String);
   }
 
+  public class UriDataSourceDesc extends android.media.DataSourceDesc {
+    method public android.content.Context getContext();
+    method public java.util.List<java.net.HttpCookie> getCookies();
+    method public java.util.Map<java.lang.String, java.lang.String> getHeaders();
+    method public android.net.Uri getUri();
+  }
+
+  public static class UriDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase {
+    ctor public UriDataSourceDesc.Builder();
+    ctor public UriDataSourceDesc.Builder(android.media.UriDataSourceDesc);
+    method public android.media.UriDataSourceDesc build();
+    method public android.media.UriDataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri);
+    method public android.media.UriDataSourceDesc.Builder setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>, java.util.List<java.net.HttpCookie>);
+  }
+
+  public final class VideoSize {
+    method public int getHeight();
+    method public int getWidth();
+  }
+
   public abstract interface VolumeAutomation {
     method public abstract android.media.VolumeShaper createVolumeShaper(android.media.VolumeShaper.Configuration);
   }
@@ -29118,10 +29369,11 @@
     field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
     field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
     field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
-    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2
-    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; // 0x3
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; // 0x4
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; // 0x2
     field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
-    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5
     field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
     field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
     field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
@@ -34049,6 +34301,7 @@
   }
 
   public final class PowerManager {
+    method public int getCurrentThermalStatus();
     method public int getLocationPowerSaveMode();
     method public boolean isDeviceIdleMode();
     method public boolean isIgnoringBatteryOptimizations(java.lang.String);
@@ -34059,6 +34312,8 @@
     method public boolean isWakeLockLevelSupported(int);
     method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
     method public void reboot(java.lang.String);
+    method public void registerThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback, java.util.concurrent.Executor);
+    method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
@@ -34073,6 +34328,18 @@
     field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
     field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
     field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
+    field public static final int THERMAL_STATUS_CRITICAL = 4; // 0x4
+    field public static final int THERMAL_STATUS_EMERGENCY = 5; // 0x5
+    field public static final int THERMAL_STATUS_LIGHT = 1; // 0x1
+    field public static final int THERMAL_STATUS_MODERATE = 2; // 0x2
+    field public static final int THERMAL_STATUS_NONE = 0; // 0x0
+    field public static final int THERMAL_STATUS_SEVERE = 3; // 0x3
+    field public static final int THERMAL_STATUS_SHUTDOWN = 6; // 0x6
+  }
+
+  public static abstract class PowerManager.ThermalStatusCallback {
+    ctor public PowerManager.ThermalStatusCallback();
+    method public void onStatusChange(int);
   }
 
   public final class PowerManager.WakeLock {
@@ -37516,8 +37783,8 @@
     field public static final java.lang.String DATE_TAKEN = "datetaken";
     field public static final java.lang.String DESCRIPTION = "description";
     field public static final java.lang.String IS_PRIVATE = "isprivate";
-    field public static final java.lang.String LATITUDE = "latitude";
-    field public static final java.lang.String LONGITUDE = "longitude";
+    field public static final deprecated java.lang.String LATITUDE = "latitude";
+    field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String ORIENTATION = "orientation";
     field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
@@ -37641,8 +37908,8 @@
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String IS_PRIVATE = "isprivate";
     field public static final java.lang.String LANGUAGE = "language";
-    field public static final java.lang.String LATITUDE = "latitude";
-    field public static final java.lang.String LONGITUDE = "longitude";
+    field public static final deprecated java.lang.String LATITUDE = "latitude";
+    field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String RESOLUTION = "resolution";
     field public static final java.lang.String TAGS = "tags";
@@ -45866,8 +46133,8 @@
     method public deprecated java.lang.String getLocale();
     method public java.util.Locale getLocaleObject();
     method public int getSpanTypeId();
-    method public int getUnderlineColor();
     method public java.lang.String[] getSuggestions();
+    method public int getUnderlineColor();
     method public void setFlags(int);
     method public void updateDrawState(android.text.TextPaint);
     method public void writeToParcel(android.os.Parcel, int);
@@ -47868,6 +48135,7 @@
     method public final boolean isFunctionPressed();
     method public static final boolean isGamepadButton(int);
     method public final boolean isLongPress();
+    method public static final boolean isMediaSessionKey(int);
     method public final boolean isMetaPressed();
     method public static boolean isModifierKey(int);
     method public final boolean isNumLockOn();
@@ -55060,6 +55328,7 @@
     method public void setInt(int, java.lang.String, int);
     method public void setIntent(int, java.lang.String, android.content.Intent);
     method public void setLabelFor(int, int);
+    method public void setLightBackgroundLayoutId(int);
     method public void setLong(int, java.lang.String, long);
     method public void setOnClickFillInIntent(int, android.content.Intent);
     method public void setOnClickPendingIntent(int, android.app.PendingIntent);
@@ -55691,6 +55960,7 @@
     method public java.lang.CharSequence getText();
     method public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
+    method public android.graphics.drawable.Drawable getTextCursorDrawable();
     method public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
@@ -55821,6 +56091,8 @@
     method public void setTextClassifier(android.view.textclassifier.TextClassifier);
     method public void setTextColor(int);
     method public void setTextColor(android.content.res.ColorStateList);
+    method public void setTextCursorDrawable(android.graphics.drawable.Drawable);
+    method public void setTextCursorDrawable(int);
     method public void setTextIsSelectable(boolean);
     method public final void setTextKeepState(java.lang.CharSequence);
     method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6ee7afa..9cf66b2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -57,6 +57,7 @@
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+    field public static final java.lang.String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
     field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
     field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
     field public static final java.lang.String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
@@ -108,6 +109,7 @@
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+    field public static final java.lang.String MANAGE_SMART_SUGGESTIONS = "android.permission.MANAGE_SMART_SUGGESTIONS";
     field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -2926,6 +2928,20 @@
     method public void stop();
   }
 
+  public static class SubtitleData.Builder {
+    ctor public SubtitleData.Builder();
+    ctor public SubtitleData.Builder(android.media.SubtitleData);
+    method public android.media.SubtitleData build();
+    method public android.media.SubtitleData.Builder setSubtitleData(int, long, long, byte[]);
+  }
+
+  public static class TimedMetaData.Builder {
+    ctor public TimedMetaData.Builder();
+    ctor public TimedMetaData.Builder(android.media.TimedMetaData);
+    method public android.media.TimedMetaData build();
+    method public android.media.TimedMetaData.Builder setTimedMetaData(int, byte[]);
+  }
+
 }
 
 package android.media.audiopolicy {
@@ -3013,6 +3029,7 @@
 package android.media.session {
 
   public final class MediaSessionManager {
+    method public android.media.session.ISession createSession(android.media.session.MediaSession.CallbackStub, java.lang.String, int);
     method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
     method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
   }
@@ -3889,14 +3906,6 @@
 
 }
 
-package android.net.wifi.p2p {
-
-  public class WifiP2pManager {
-    method public void factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
-  }
-
-}
-
 package android.net.wifi.rtt {
 
   public static final class RangingRequest.Builder {
@@ -4629,6 +4638,7 @@
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+    field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled";
     field public static final java.lang.String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -4658,18 +4668,18 @@
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final java.lang.String APN_SET_ID = "apn_set_id";
     field public static final int CARRIER_EDITED = 4; // 0x4
-    field public static final java.lang.String EDITED = "edited";
-    field public static final java.lang.String MAX_CONNS = "max_conns";
-    field public static final java.lang.String MAX_CONNS_TIME = "max_conns_time";
-    field public static final java.lang.String MODEM_COGNITIVE = "modem_cognitive";
+    field public static final java.lang.String EDITED_STATUS = "edited";
+    field public static final java.lang.String MAX_CONNECTIONS = "max_conns";
+    field public static final java.lang.String MODEM_PERSIST = "modem_cognitive";
     field public static final java.lang.String MTU = "mtu";
-    field public static final int NO_SET_SET = 0; // 0x0
+    field public static final int NO_APN_SET_ID = 0; // 0x0
+    field public static final java.lang.String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
     field public static final int UNEDITED = 0; // 0x0
     field public static final int USER_DELETED = 2; // 0x2
     field public static final java.lang.String USER_EDITABLE = "user_editable";
     field public static final int USER_EDITED = 1; // 0x1
     field public static final java.lang.String USER_VISIBLE = "user_visible";
-    field public static final java.lang.String WAIT_TIME = "wait_time";
+    field public static final java.lang.String WAIT_TIME_RETRY = "wait_time";
   }
 
   public final class TimeZoneRulesDataContract {
@@ -4996,6 +5006,7 @@
   }
 
   public final class FillRequest {
+    method public android.view.autofill.AutofillValue getFocusedAutofillValue();
     method public android.view.autofill.AutofillId getFocusedId();
     method public android.service.intelligence.PresentationParams getPresentationParams();
     method public android.service.intelligence.InteractionSessionId getSessionId();
@@ -5784,6 +5795,8 @@
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
+    method public void setDefaultDataSubId(int);
+    method public void setDefaultSmsSubId(int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideUnmetered(int, boolean, long);
     method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
@@ -7358,22 +7371,8 @@
     ctor public SslErrorHandler();
   }
 
-  public abstract class TokenBindingService {
+  public abstract deprecated class TokenBindingService {
     ctor public TokenBindingService();
-    method public abstract void deleteAllKeys(android.webkit.ValueCallback<java.lang.Boolean>);
-    method public abstract void deleteKey(android.net.Uri, android.webkit.ValueCallback<java.lang.Boolean>);
-    method public abstract void enableTokenBinding();
-    method public static android.webkit.TokenBindingService getInstance();
-    method public abstract void getKey(android.net.Uri, java.lang.String[], android.webkit.ValueCallback<android.webkit.TokenBindingService.TokenBindingKey>);
-    field public static final java.lang.String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
-    field public static final java.lang.String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5";
-    field public static final java.lang.String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS";
-  }
-
-  public static abstract class TokenBindingService.TokenBindingKey {
-    ctor public TokenBindingService.TokenBindingKey();
-    method public abstract java.lang.String getAlgorithm();
-    method public abstract java.security.KeyPair getKeyPair();
   }
 
   public class WebChromeClient {
@@ -7503,7 +7502,7 @@
     method public abstract android.webkit.GeolocationPermissions getGeolocationPermissions();
     method public abstract android.webkit.ServiceWorkerController getServiceWorkerController();
     method public abstract android.webkit.WebViewFactoryProvider.Statics getStatics();
-    method public abstract android.webkit.TokenBindingService getTokenBindingService();
+    method public abstract deprecated android.webkit.TokenBindingService getTokenBindingService();
     method public abstract android.webkit.TracingController getTracingController();
     method public abstract android.webkit.WebIconDatabase getWebIconDatabase();
     method public abstract android.webkit.WebStorage getWebStorage();
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
index 1e970f4..40da583 100644
--- a/cmds/incidentd/Android.bp
+++ b/cmds/incidentd/Android.bp
@@ -53,6 +53,7 @@
         "libprotoutil",
         "libservices",
         "libutils",
+        "libprotobuf-cpp-lite",
     ],
 
     init_rc: ["incidentd.rc"],
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index a8ef831..74cda1d 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -47,9 +47,13 @@
     while (true) {
         if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
             mTruncated = true;
+            VLOG("Truncating data");
             break;
         }
-        if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
+        if (mBuffer.writeBuffer() == NULL) {
+            VLOG("No memory");
+            return NO_MEMORY;
+        }
 
         int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
         if (remainingTime <= 0) {
@@ -58,7 +62,7 @@
             break;
         }
 
-        int count = poll(&pfds, 1, remainingTime);
+        int count = TEMP_FAILURE_RETRY(poll(&pfds, 1, remainingTime));
         if (count == 0) {
             VLOG("timed out due to block calling poll");
             mTimedOut = true;
@@ -102,7 +106,10 @@
             VLOG("Truncating data");
             break;
         }
-        if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
+        if (mBuffer.writeBuffer() == NULL) {
+            VLOG("No memory");
+            return NO_MEMORY;
+        }
 
         ssize_t amt =
                 TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()));
@@ -144,10 +151,14 @@
     // This is the buffer used to store processed data
     while (true) {
         if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+            VLOG("Truncating data");
             mTruncated = true;
             break;
         }
-        if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
+        if (mBuffer.writeBuffer() == NULL) {
+            VLOG("No memory");
+            return NO_MEMORY;
+        }
 
         int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
         if (remainingTime <= 0) {
@@ -157,7 +168,7 @@
         }
 
         // wait for any pfds to be ready to perform IO
-        int count = poll(pfds, 3, remainingTime);
+        int count = TEMP_FAILURE_RETRY(poll(pfds, 3, remainingTime));
         if (count == 0) {
             VLOG("timed out due to block calling poll");
             mTimedOut = true;
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index b23c1ed..3d892ad 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -42,13 +42,11 @@
 #define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024)        // 20MB
 #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000)  // 1 Day
 
-// Skip logs (1100 - 1108) because they are already in the bug report
-// Skip 1200, 1201, 1202, 3018 because they take too long
-// TODO(120079956): Skip 3008, 3015 because of error
+// Skip these sections for dumpstate only. Dumpstate allows 10s max for each service to dump.
+// Skip logs (1100 - 1108) and traces (1200 - 1202) because they are already in the bug report.
+// Skip 3018 because it takes too long.
 #define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
                            1200, 1201, 1202, /* Native, hal, java traces */ \
-                           3008, /* "package --proto" */ \
-                           3015, /* "activity --proto processes" */ \
                            3018  /* "meminfo -a --proto" */ }
 
 namespace android {
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 7a8ebe3..6967e33 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -96,7 +96,12 @@
     uint64_t token = mProto.start(encode_field_id(policy));
     while (mData.rp()->pos() - start != msgSize) {
         status_t err = stripField(policy, spec, depth + 1);
-        if (err != NO_ERROR) return err;
+        if (err != NO_ERROR) {
+            VLOG("Bad value when stripping id %d, wiretype %d, tag %#x, depth %d, size %d, "
+                "relative pos %zu, ", fieldId, read_wire_type(fieldTag), fieldTag, depth,
+                msgSize, mData.rp()->pos() - start);
+            return err;
+        }
     }
     mProto.end(token);
     return NO_ERROR;
@@ -117,9 +122,13 @@
     }
     while (mData.hasNext()) {
         status_t err = stripField(mPolicy, spec, 0);
-        if (err != NO_ERROR) return err;
+        if (err != NO_ERROR) return err; // Error logged in stripField.
     }
-    if (mData.bytesRead() != mData.size()) return BAD_VALUE;
+    if (mData.bytesRead() != mData.size()) {
+        VLOG("Buffer corrupted: expect %zu bytes, read %zu bytes",
+            mData.size(), mData.bytesRead());
+        return BAD_VALUE;
+    }
     mSize = mProto.size();
     mData.rp()->rewind();  // rewind the read pointer back to beginning after the strip.
     return NO_ERROR;
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index b3bab0c..8f62da2 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -129,6 +129,7 @@
     bool needMainFd = false;
     int mainFd = -1;
     int mainDest = -1;
+    int sectionCount = 0;
     HeaderSection headers;
     MetadataSection metadataSection;
     std::string buildType = android::base::GetProperty("ro.build.type", "");
@@ -180,12 +181,12 @@
     for (const Section** section = SECTION_LIST; *section; section++) {
         const int id = (*section)->id;
         if ((*section)->userdebugAndEngOnly && !isUserdebugOrEng) {
-            ALOGD("Skipping incident report section %d '%s' because it's limited to userdebug/eng",
+            VLOG("Skipping incident report section %d '%s' because it's limited to userdebug/eng",
                   id, (*section)->name.string());
             continue;
         }
         if (this->batch.containsSection(id)) {
-            ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
+            VLOG("Taking incident report section %d '%s'", id, (*section)->name.string());
             for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
                 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
                     (*it)->listener->onReportSectionStatus(
@@ -198,11 +199,12 @@
             int64_t startTime = uptimeMillis();
             err = (*section)->Execute(&batch);
             int64_t endTime = uptimeMillis();
-            stats->set_success(err == NO_ERROR);
             stats->set_exec_duration_ms(endTime - startTime);
             if (err != NO_ERROR) {
                 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
                       (*section)->name.string(), id, strerror(-err));
+                // Execute() has already recorded this status. Only update if there's new failure.
+                stats->set_success(false);
                 goto DONE;
             }
             (*reportByteSize) += stats->report_size_bytes();
@@ -214,11 +216,13 @@
                             id, IIncidentReportStatusListener::STATUS_FINISHED);
                 }
             }
-            ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
+            VLOG("Finish incident report section %d '%s'", id, (*section)->name.string());
+            sectionCount++;
         }
     }
 
 DONE:
+    ALOGD("Incident reporting took %d sections.", sectionCount);
     // Reports the metdadata when taking the incident report.
     if (!isTest) metadataSection.Execute(&batch);
 
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index cd48af9..10d2268 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -75,6 +75,7 @@
     stats->set_dump_duration_ms(buffer.durationMs());
     stats->set_timed_out(buffer.timedOut());
     stats->set_is_truncated(buffer.truncated());
+    stats->set_success(!buffer.timedOut() && !buffer.truncated());
 }
 
 // Reads data from FdBuffer and writes it to the requests file descriptor.
@@ -83,7 +84,8 @@
     status_t err = -EBADF;
     EncodedBuffer::iterator data = buffer.data();
     PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
-    int writeable = 0;
+    IncidentMetadata::SectionStats* stats = requests->sectionStats(id);
+    int nPassed = 0;
 
     // The streaming ones, group requests by spec in order to save unnecessary strip operations
     map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
@@ -99,7 +101,18 @@
     for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) {
         PrivacySpec spec = mit->first;
         err = privacyBuffer.strip(spec);
-        if (err != NO_ERROR) return err;  // it means the privacyBuffer data is corrupted.
+        if (err != NO_ERROR) {
+            // Privacy Buffer is corrupted, probably due to an ill-formatted proto. This is a
+            // non-fatal error. The whole report is still valid. So just log the failure.
+            ALOGW("Failed to strip section %d with spec %d: %s",
+                id, spec.dest, strerror(-err));
+            stats->set_success(false);
+            stats->set_error_msg("Failed to strip section: privacy buffer corrupted, probably "
+                "due to an ill-formatted proto");
+            nPassed++;
+            continue;
+        }
+
         if (privacyBuffer.size() == 0) continue;
 
         for (auto it = mit->second.begin(); it != mit->second.end(); it++) {
@@ -114,7 +127,7 @@
                 request->err = err;
                 continue;
             }
-            writeable++;
+            nPassed++;
             VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(),
                  request->fd, spec.dest);
         }
@@ -125,7 +138,15 @@
     if (requests->mainFd() >= 0) {
         PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest());
         err = privacyBuffer.strip(spec);
-        if (err != NO_ERROR) return err;  // the buffer data is corrupted.
+        if (err != NO_ERROR) {
+            ALOGW("Failed to strip section %d with spec %d for dropbox: %s",
+                id, spec.dest, strerror(-err));
+            stats->set_success(false);
+            stats->set_error_msg("Failed to strip section: privacy buffer corrupted, probably "
+                "due to an ill-formatted proto");
+            nPassed++;
+            goto DONE;
+        }
         if (privacyBuffer.size() == 0) goto DONE;
 
         err = write_section_header(requests->mainFd(), id, privacyBuffer.size());
@@ -138,7 +159,7 @@
             requests->setMainFd(-1);
             goto DONE;
         }
-        writeable++;
+        nPassed++;
         VLOG("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(),
              requests->mainFd(), spec.dest);
         // Reports bytes of the section uploaded via dropbox after filtering.
@@ -147,7 +168,7 @@
 
 DONE:
     // only returns error if there is no fd to write to.
-    return writeable > 0 ? NO_ERROR : err;
+    return nPassed > 0 ? NO_ERROR : err;
 }
 
 // ================================================================================
@@ -213,6 +234,8 @@
                     stats.timed_out());
         proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kIsTruncatedFieldNumber,
                     stats.is_truncated());
+        proto.write(FIELD_TYPE_STRING | IncidentMetadata::SectionStats::kErrorMsgFieldNumber,
+                    stats.error_msg());
         proto.end(token);
     }
 
@@ -439,8 +462,9 @@
     status_t err = NO_ERROR;
     pthread_t thread;
     pthread_attr_t attr;
-    bool timedOut = false;
+    bool workerDone = false;
     FdBuffer buffer;
+    IncidentMetadata::SectionStats* stats = requests->sectionStats(this->id);
 
     // Data shared between this thread and the worker thread.
     sp<WorkerThreadData> data = new WorkerThreadData(this);
@@ -475,8 +499,7 @@
     // Loop reading until either the timeout or the worker side is done (i.e. eof).
     err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
     if (err != NO_ERROR) {
-        // TODO: Log this error into the incident report.
-        ALOGW("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
+        ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
     }
 
     // Done with the read fd. The worker thread closes the write one so
@@ -484,31 +507,32 @@
     data->pipe.readFd().reset();
 
     // If the worker side is finished, then return its error (which may overwrite
-    // our possible error -- but it's more interesting anyway).  If not, then we timed out.
+    // our possible error -- but it's more interesting anyway). If not, then we timed out.
     {
         unique_lock<mutex> lock(data->lock);
-        if (!data->workerDone) {
-            // We timed out
-            timedOut = true;
-        } else {
-            if (data->workerError != NO_ERROR) {
-                err = data->workerError;
-                // TODO: Log this error into the incident report.
-                ALOGW("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
-            }
+        if (data->workerError != NO_ERROR) {
+            err = data->workerError;
+            ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
         }
+        workerDone = data->workerDone;
     }
 
-    write_section_stats(requests->sectionStats(this->id), buffer);
-    if (timedOut || buffer.timedOut()) {
-        ALOGW("[%s] timed out", this->name.string());
+    write_section_stats(stats, buffer);
+    if (err != NO_ERROR) {
+        char errMsg[128];
+        snprintf(errMsg, 128, "[%s] failed with error '%s'",
+            this->name.string(), strerror(-err));
+        stats->set_success(false);
+        stats->set_error_msg(errMsg);
         return NO_ERROR;
     }
-
-    // TODO: There was an error with the command or buffering. Report that.  For now
-    // just exit with a log messasge.
-    if (err != NO_ERROR) {
-        ALOGW("[%s] failed with error '%s'", this->name.string(), strerror(-err));
+    if (buffer.truncated()) {
+        ALOGW("[%s] too large, truncating", this->name.string());
+        // Do not write a truncated section. It won't pass through the PrivacyBuffer.
+        return NO_ERROR;
+    }
+    if (!workerDone || buffer.timedOut()) {
+        ALOGW("[%s] timed out", this->name.string());
         return NO_ERROR;
     }
 
@@ -617,14 +641,8 @@
     sp<IBinder> service = defaultServiceManager()->checkService(mService);
 
     if (service == NULL) {
-        // Returning an error interrupts the entire incident report, so just
-        // log the failure.
-        // TODO: have a meta record inside the report that would log this
-        // failure inside the report, because the fact that we can't find
-        // the service is good data in and of itself. This is running in
-        // another thread so lock that carefully...
         ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
-        return NO_ERROR;
+        return NAME_NOT_FOUND;
     }
 
     service->dump(pipeWriteFd, mArgs);
diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-greylist-max-p.txt
similarity index 100%
rename from config/hiddenapi-max-sdk-p-blacklist.txt
rename to config/hiddenapi-greylist-max-p.txt
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-greylist.txt
similarity index 94%
rename from config/hiddenapi-light-greylist.txt
rename to config/hiddenapi-greylist.txt
index e10f729..579ef93 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -51,6 +51,7 @@
 Landroid/app/backup/IFullBackupRestoreObserver$Stub;-><init>()V
 Landroid/app/backup/IRestoreObserver$Stub;-><init>()V
 Landroid/app/DownloadManager;->restartDownload([J)V
+Landroid/app/IActivityController$Stub;-><init>()V
 Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
 Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
@@ -62,25 +63,35 @@
 Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
 Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
 Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
+Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
+Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
 Landroid/app/IActivityManager;->checkPermission(Ljava/lang/String;II)I
+Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
 Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
 Landroid/app/IActivityManager;->finishHeavyWeightApp()V
 Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
 Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
 Landroid/app/IActivityManager;->getAllStackInfos()Ljava/util/List;
 Landroid/app/IActivityManager;->getConfiguration()Landroid/content/res/Configuration;
+Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
+Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
 Landroid/app/IActivityManager;->getIntentForIntentSender(Landroid/content/IIntentSender;)Landroid/content/Intent;
 Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender;
 Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
 Landroid/app/IActivityManager;->getLaunchedFromUid(Landroid/os/IBinder;)I
+Landroid/app/IActivityManager;->getLockTaskModeState()I
 Landroid/app/IActivityManager;->getMemoryInfo(Landroid/app/ActivityManager$MemoryInfo;)V
 Landroid/app/IActivityManager;->getPackageProcessState(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/app/IActivityManager;->getProcessLimit()I
+Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
 Landroid/app/IActivityManager;->getProcessPss([I)[J
 Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String;
+Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
+Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
 Landroid/app/IActivityManager;->getServices(II)Ljava/util/List;
 Landroid/app/IActivityManager;->getTaskBounds(I)Landroid/graphics/Rect;
 Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
+Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/app/IActivityManager;->handleApplicationStrictModeViolation(Landroid/os/IBinder;ILandroid/os/StrictMode$ViolationInfo;)V
 Landroid/app/IActivityManager;->hang(Landroid/os/IBinder;Z)V
 Landroid/app/IActivityManager;->isInLockTaskMode()Z
@@ -98,9 +109,11 @@
 Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
 Landroid/app/IActivityManager;->registerProcessObserver(Landroid/app/IProcessObserver;)V
 Landroid/app/IActivityManager;->registerReceiver(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/IIntentReceiver;Landroid/content/IntentFilter;Ljava/lang/String;II)Landroid/content/Intent;
+Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
 Landroid/app/IActivityManager;->registerUserSwitchObserver(Landroid/app/IUserSwitchObserver;Ljava/lang/String;)V
 Landroid/app/IActivityManager;->removeContentProviderExternal(Ljava/lang/String;Landroid/os/IBinder;)V
 Landroid/app/IActivityManager;->removeStack(I)V
+Landroid/app/IActivityManager;->removeTask(I)Z
 Landroid/app/IActivityManager;->requestBugReport(I)V
 Landroid/app/IActivityManager;->resizeDockedStack(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
 Landroid/app/IActivityManager;->resizeStack(ILandroid/graphics/Rect;ZZZI)V
@@ -120,8 +133,12 @@
 Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
 Landroid/app/IActivityManager;->setTaskResizeable(II)V
 Landroid/app/IActivityManager;->shutdown(I)Z
+Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
+Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
+Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
 Landroid/app/IActivityManager;->startBinderTracking()Z
 Landroid/app/IActivityManager;->startInstrumentation(Landroid/content/ComponentName;Ljava/lang/String;ILandroid/os/Bundle;Landroid/app/IInstrumentationWatcher;Landroid/app/IUiAutomationConnection;ILjava/lang/String;)Z
+Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
 Landroid/app/IActivityManager;->startSystemLockTaskMode(I)V
 Landroid/app/IActivityManager;->startUserInBackground(I)Z
 Landroid/app/IActivityManager;->stopAppSwitches()V
@@ -144,12 +161,16 @@
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
 Landroid/app/IAlarmManager;->getNextAlarmClock(I)Landroid/app/AlarmManager$AlarmClockInfo;
 Landroid/app/IAlarmManager;->set(Ljava/lang/String;IJJJILandroid/app/PendingIntent;Landroid/app/IAlarmListener;Ljava/lang/String;Landroid/os/WorkSource;Landroid/app/AlarmManager$AlarmClockInfo;)V
+Landroid/app/IAlarmManager;->setTime(J)Z
 Landroid/app/IApplicationThread;->scheduleBindService(Landroid/os/IBinder;Landroid/content/Intent;ZI)V
 Landroid/app/IApplicationThread;->scheduleCreateService(Landroid/os/IBinder;Landroid/content/pm/ServiceInfo;Landroid/content/res/CompatibilityInfo;I)V
 Landroid/app/IApplicationThread;->scheduleStopService(Landroid/os/IBinder;)V
 Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
 Landroid/app/IApplicationThread;->scheduleUnbindService(Landroid/os/IBinder;Landroid/content/Intent;)V
 Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo;
+Landroid/app/IAssistDataReceiver$Stub;-><init>()V
+Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
+Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
 Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
 Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
 Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
@@ -236,6 +257,7 @@
 Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->isEnabled()Z
+Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
 Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp;
 Landroid/bluetooth/IBluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
@@ -266,6 +288,10 @@
 Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
 Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
+Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
 Landroid/content/ContentProviderProxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
@@ -312,11 +338,13 @@
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
+Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
 Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
 Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
 Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
 Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
 Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
@@ -325,6 +353,7 @@
 Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
 Landroid/content/pm/IPackageDeleteObserver2$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver2;
 Landroid/content/pm/IPackageDeleteObserver2;->onPackageDeleted(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
 Landroid/content/pm/IPackageInstaller;->uninstall(Landroid/content/pm/VersionedPackage;Ljava/lang/String;ILandroid/content/IntentSender;I)V
 Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
@@ -364,11 +393,14 @@
 Landroid/content/pm/IPackageManager;->clearPackagePreferredActivities(Ljava/lang/String;)V
 Landroid/content/pm/IPackageManager;->currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->deleteApplicationCacheFiles(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
 Landroid/content/pm/IPackageManager;->getApplicationEnabledSetting(Ljava/lang/String;I)I
+Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
 Landroid/content/pm/IPackageManager;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->getBlockUninstallForUser(Ljava/lang/String;I)Z
 Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I
 Landroid/content/pm/IPackageManager;->getFlagsForUid(I)I
+Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
 Landroid/content/pm/IPackageManager;->getInstalledApplications(II)Landroid/content/pm/ParceledListSlice;
 Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
 Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;
@@ -376,6 +408,7 @@
 Landroid/content/pm/IPackageManager;->getInstrumentationInfo(Landroid/content/ComponentName;I)Landroid/content/pm/InstrumentationInfo;
 Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
 Landroid/content/pm/IPackageManager;->getNameForUid(I)Ljava/lang/String;
+Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/IPackageManager;->getPackageInstaller()Landroid/content/pm/IPackageInstaller;
 Landroid/content/pm/IPackageManager;->getPackagesForUid(I)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->getPackageUid(Ljava/lang/String;II)I
@@ -415,6 +448,7 @@
 Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
 Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
 Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
+Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
 Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
 Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V
@@ -438,6 +472,7 @@
 Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
 Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
 Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
 Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
 Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
@@ -447,7 +482,10 @@
 Landroid/location/ICountryListener$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider;
+Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
+Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeofenceProvider$Stub;-><init>()V
+Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
 Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/location/ILocationListener$Stub;-><init>()V
@@ -460,6 +498,10 @@
 Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
 Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
 Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
+Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
+Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
+Landroid/location/INetInitiatedListener$Stub;-><init>()V
+Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
 Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
 Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
 Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
@@ -486,6 +528,17 @@
 Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V
 Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z
 Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager;
+Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
+Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
+Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
 Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
 Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
@@ -499,6 +552,7 @@
 Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
 Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
 Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I
 Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String;
@@ -508,9 +562,12 @@
 Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->reportInetCondition(II)V
 Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
+Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
+Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
 Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
 Landroid/net/INetworkPolicyListener$Stub;-><init>()V
 Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
+Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
 Landroid/net/INetworkPolicyManager;->getRestrictBackground()Z
 Landroid/net/INetworkPolicyManager;->getUidPolicy(I)I
 Landroid/net/INetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V
@@ -520,14 +577,19 @@
 Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService;
 Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
 Landroid/net/INetworkStatsService;->forceUpdate()V
 Landroid/net/INetworkStatsService;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats;
 Landroid/net/INetworkStatsService;->getMobileIfaces()[Ljava/lang/String;
 Landroid/net/INetworkStatsService;->openSession()Landroid/net/INetworkStatsSession;
 Landroid/net/INetworkStatsService;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession;
 Landroid/net/INetworkStatsSession;->close()V
+Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
+Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
 Landroid/net/INetworkStatsSession;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats;
 Landroid/net/INetworkStatsSession;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
+Landroid/net/InterfaceConfiguration;-><init>()V
+Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
 Landroid/net/MobileLinkQualityInfo;-><init>()V
 Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
 Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger;
@@ -552,7 +614,12 @@
 Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
 Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
 Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
+Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
+Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
 Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->result:Ljava/lang/Object;
+Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
 Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
 Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
 Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
@@ -569,18 +636,24 @@
 Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_VOLTAGE:Ljava/lang/String;
 Landroid/os/BatteryStats$Counter;-><init>()V
 Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
+Landroid/os/BatteryStats$HistoryItem;-><init>()V
 Landroid/os/BatteryStats$HistoryItem;->batteryHealth:B
+Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
 Landroid/os/BatteryStats$HistoryItem;->batteryPlugType:B
 Landroid/os/BatteryStats$HistoryItem;->batteryStatus:B
 Landroid/os/BatteryStats$HistoryItem;->batteryVoltage:C
 Landroid/os/BatteryStats$HistoryItem;->clear()V
+Landroid/os/BatteryStats$HistoryItem;->cmd:B
 Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
 Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem;
 Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z
 Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V
 Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V
 Landroid/os/BatteryStats$HistoryItem;->states2:I
+Landroid/os/BatteryStats$HistoryItem;->states:I
+Landroid/os/BatteryStats$HistoryItem;->time:J
 Landroid/os/BatteryStats$Timer;-><init>()V
+Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
 Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStarts(I)I
@@ -604,6 +677,8 @@
 Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid$Sensor;->GPS:I
 Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V
+Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
+Landroid/os/BatteryStats$Uid;-><init>()V
 Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
 Landroid/os/BatteryStats$Uid;->getMobileRadioActiveTime(I)J
@@ -616,6 +691,7 @@
 Landroid/os/BatteryStats$Uid;->getWakelockStats()Landroid/util/ArrayMap;
 Landroid/os/BatteryStats$Uid;->getWifiBatchedScanTime(IJI)J
 Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
 Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
 Landroid/os/BatteryStats;-><init>()V
 Landroid/os/BatteryStats;->computeBatteryRealtime(JI)J
@@ -627,6 +703,7 @@
 Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J
 Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
 Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J
+Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
 Landroid/os/BatteryStats;->getPhoneOnTime(JI)J
 Landroid/os/BatteryStats;->getPhoneSignalStrengthTime(IJI)J
 Landroid/os/BatteryStats;->getScreenBrightnessTime(IJI)J
@@ -640,6 +717,10 @@
 Landroid/os/BatteryStats;->WAKE_TYPE_PARTIAL:I
 Landroid/os/Binder;->execTransact(IJJI)Z
 Landroid/os/Binder;->mObject:J
+Landroid/os/Broadcaster;-><init>()V
+Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
+Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
+Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
 Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
 Landroid/os/Build;->getLong(Ljava/lang/String;)J
 Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
@@ -713,6 +794,7 @@
 Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
+Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
 Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
 Landroid/os/Environment;->initForCurrentUser()V
 Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
@@ -733,12 +815,15 @@
 Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
 Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z
+Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
 Landroid/os/Handler;-><init>(Z)V
 Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+Landroid/os/Handler;->getMain()Landroid/os/Handler;
 Landroid/os/Handler;->getPostMessage(Ljava/lang/Runnable;Ljava/lang/Object;)Landroid/os/Message;
 Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
 Landroid/os/Handler;->mLooper:Landroid/os/Looper;
 Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
+Landroid/os/HwBinder;->reportSyspropChanged()V
 Landroid/os/HwParcel;-><init>(Z)V
 Landroid/os/HwRemoteBinder;-><init>()V
 Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -749,16 +834,26 @@
 Landroid/os/IDeviceIdleController;->getAppIdTempWhitelist()[I
 Landroid/os/IDeviceIdleController;->getFullPowerWhitelistExceptIdle()[Ljava/lang/String;
 Landroid/os/INetworkManagementService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
+Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->disableNat(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->enableNat(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->getInterfaceConfig(Ljava/lang/String;)Landroid/net/InterfaceConfiguration;
 Landroid/os/INetworkManagementService;->getIpForwardingEnabled()Z
+Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
 Landroid/os/INetworkManagementService;->isTetheringStarted()Z
 Landroid/os/INetworkManagementService;->listTetheredInterfaces()[Ljava/lang/String;
+Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
+Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
+Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
 Landroid/os/INetworkManagementService;->setIpForwardingEnabled(Z)V
+Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
 Landroid/os/INetworkManagementService;->startTethering([Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->stopTethering()V
 Landroid/os/INetworkManagementService;->tetherInterface(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
 Landroid/os/INetworkManagementService;->untetherInterface(Ljava/lang/String;)V
 Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z
 Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController;
@@ -767,12 +862,15 @@
 Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
 Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
 Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
+Landroid/os/IPowerManager;->goToSleep(JII)V
 Landroid/os/IPowerManager;->isInteractive()Z
 Landroid/os/IPowerManager;->nap(J)V
+Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
 Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V
 Landroid/os/IPowerManager;->userActivity(JII)V
 Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V
 Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem;
+Landroid/os/IRemoteCallback$Stub;-><init>()V
 Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V
 Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
@@ -798,6 +896,7 @@
 Landroid/os/Message;->markInUse()V
 Landroid/os/Message;->next:Landroid/os/Message;
 Landroid/os/Message;->recycleUnchecked()V
+Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
 Landroid/os/Message;->target:Landroid/os/Handler;
 Landroid/os/Message;->toString(J)Ljava/lang/String;
 Landroid/os/Message;->when:J
@@ -817,13 +916,16 @@
 Landroid/os/Parcel;->mNativePtr:J
 Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
 Landroid/os/Parcel;->readArraySet(Ljava/lang/ClassLoader;)Landroid/util/ArraySet;
+Landroid/os/Parcel;->readBlob()[B
 Landroid/os/Parcel;->readCharSequence()Ljava/lang/CharSequence;
 Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable;
 Landroid/os/Parcel;->readExceptionCode()I
 Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator;
 Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
 Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
 Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V
+Landroid/os/Parcel;->writeBlob([B)V
 Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V
 Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V
 Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
@@ -890,15 +992,27 @@
 Landroid/os/Process;->VPN_UID:I
 Landroid/os/Process;->WIFI_UID:I
 Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
+Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/Registrant;->clear()V
 Landroid/os/Registrant;->getHandler()Landroid/os/Handler;
 Landroid/os/Registrant;->messageForRegistrant()Landroid/os/Message;
+Landroid/os/Registrant;->notifyRegistrant()V
+Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
 Landroid/os/Registrant;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;-><init>()V
 Landroid/os/RegistrantList;->add(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
+Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
 Landroid/os/RegistrantList;->get(I)Ljava/lang/Object;
+Landroid/os/RegistrantList;->notifyRegistrants()V
+Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
 Landroid/os/RegistrantList;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
+Landroid/os/RegistrantList;->removeCleared()V
 Landroid/os/RegistrantList;->size()I
 Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler;
 Landroid/os/RemoteCallbackList;->mCallbacks:Landroid/util/ArrayMap;
+Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
 Landroid/os/SELinux;->checkSELinuxAccess(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
 Landroid/os/SELinux;->getContext()Ljava/lang/String;
 Landroid/os/SELinux;->getFileContext(Ljava/lang/String;)Ljava/lang/String;
@@ -920,6 +1034,7 @@
 Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
 Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
+Landroid/os/ServiceSpecificException;->errorCode:I
 Landroid/os/SharedMemory;->getFd()I
 Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
 Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
@@ -927,6 +1042,7 @@
 Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener;
 Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
+Landroid/os/storage/StorageEventListener;-><init>()V
 Landroid/os/StrictMode$Span;->finish()V
 Landroid/os/StrictMode$ThreadPolicy;->mask:I
 Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I
@@ -954,8 +1070,12 @@
 Landroid/os/SystemProperties;->native_get_long(Ljava/lang/String;J)J
 Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/SystemProperties;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->reportSyspropChanged()V
 Landroid/os/SystemProperties;->sChangeCallbacks:Ljava/util/ArrayList;
 Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/SystemService;->start(Ljava/lang/String;)V
+Landroid/os/SystemService;->stop(Ljava/lang/String;)V
+Landroid/os/SystemVibrator;-><init>()V
 Landroid/os/SystemVibrator;-><init>(Landroid/content/Context;)V
 Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
 Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
@@ -994,6 +1114,7 @@
 Landroid/os/UserHandle;->getUid(II)I
 Landroid/os/UserHandle;->getUserId(I)I
 Landroid/os/UserHandle;->isIsolated(I)Z
+Landroid/os/UserHandle;->isSameApp(II)Z
 Landroid/os/UserHandle;->mHandle:I
 Landroid/os/UserHandle;->MU_ENABLED:Z
 Landroid/os/UserHandle;->OWNER:Landroid/os/UserHandle;
@@ -1023,6 +1144,7 @@
 Landroid/os/UserManager;->getUserStartRealtime()J
 Landroid/os/UserManager;->getUserUnlockRealtime()J
 Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
 Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
 Landroid/os/UserManager;->isGuestUser(I)Z
 Landroid/os/UserManager;->isLinkedUser()Z
@@ -1072,6 +1194,7 @@
 Landroid/R$styleable;->CalendarView_weekDayTextAppearance:I
 Landroid/R$styleable;->CalendarView_weekNumberColor:I
 Landroid/R$styleable;->CalendarView_weekSeparatorLineColor:I
+Landroid/R$styleable;->CheckBoxPreference:[I
 Landroid/R$styleable;->CheckedTextView:[I
 Landroid/R$styleable;->CheckedTextView_checkMark:I
 Landroid/R$styleable;->CompoundButton:[I
@@ -1382,7 +1505,18 @@
 Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
+Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
+Landroid/telephony/ims/compat/ImsService;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
 Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable;)V
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
 Landroid/telephony/SmsCbCmasInfo;->getCategory()I
 Landroid/telephony/SmsCbCmasInfo;->getCertainty()I
 Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I
@@ -1424,10 +1558,21 @@
 Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V
 Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
+Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
 Landroid/view/IDockedStackListener$Stub;-><init>()V
 Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats;
+Landroid/view/IRecentsAnimationController;->finish(Z)V
+Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
+Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
+Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
+Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
 Landroid/view/IRotationWatcher$Stub;-><init>()V
 Landroid/view/IWindow$Stub;-><init>()V
 Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow;
@@ -1439,6 +1584,9 @@
 Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I
 Landroid/view/IWindowManager$Stub;-><init>()V
 Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
+Landroid/view/IWindowManager;->endProlongedAnimations()V
 Landroid/view/IWindowManager;->executeAppTransition()V
 Landroid/view/IWindowManager;->freezeRotation(I)V
 Landroid/view/IWindowManager;->getAnimationScale(I)F
@@ -1447,16 +1595,20 @@
 Landroid/view/IWindowManager;->getDockedStackSide()I
 Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
 Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
+Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
 Landroid/view/IWindowManager;->hasNavigationBar(I)Z
 Landroid/view/IWindowManager;->isKeyguardLocked()Z
 Landroid/view/IWindowManager;->isKeyguardSecure()Z
 Landroid/view/IWindowManager;->isSafeModeEnabled()Z
 Landroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 Landroid/view/IWindowManager;->registerDockedStackListener(Landroid/view/IDockedStackListener;)V
 Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V
 Landroid/view/IWindowManager;->setAnimationScale(IF)V
 Landroid/view/IWindowManager;->setAnimationScales([F)V
 Landroid/view/IWindowManager;->setInTouchMode(Z)V
+Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
 Landroid/view/IWindowManager;->setShelfHeight(ZI)V
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
@@ -1496,6 +1648,7 @@
 Lcom/android/ims/ImsCall;->reject(I)V
 Lcom/android/ims/ImsCall;->terminate(I)V
 Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
+Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
 Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V
 Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig;
 Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager;
@@ -1505,12 +1658,48 @@
 Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V
 Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
 Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession;
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
 Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
 Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnected()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
 Lcom/android/ims/internal/IImsService$Stub;-><init>()V
 Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService;
 Lcom/android/ims/internal/IImsUt$Stub;-><init>()V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsVideoCallCallback;->changeCallDataUsage(J)V
 Lcom/android/ims/internal/IImsVideoCallCallback;->changeCameraCapabilities(Landroid/telecom/VideoProfile$CameraCapabilities;)V
 Lcom/android/ims/internal/IImsVideoCallCallback;->changePeerDimensions(II)V
@@ -1521,7 +1710,52 @@
 Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
 Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
 Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
+Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
+Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
 Lcom/android/internal/app/AlertActivity;-><init>()V
 Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController;
 Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams;
@@ -1552,6 +1786,7 @@
 Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startOperation:I
 Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startWatchingMode:I
 Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_stopWatchingMode:I
+Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
 Lcom/android/internal/app/IAppOpsService;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
 Lcom/android/internal/app/IAppOpsService;->getPackagesForOps([I)Ljava/util/List;
 Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V
@@ -1599,6 +1834,7 @@
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
 Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
+Lcom/android/internal/content/PackageMonitor;-><init>()V
 Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V
 Lcom/android/internal/database/SortCursor;->mCursor:Landroid/database/Cursor;
 Lcom/android/internal/database/SortCursor;->mCursors:[Landroid/database/Cursor;
@@ -1612,6 +1848,14 @@
 Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;)V
 Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
 Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
+Lcom/android/internal/location/ILocationProvider;->disable()V
+Lcom/android/internal/location/ILocationProvider;->enable()V
+Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
+Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
+Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
 Lcom/android/internal/logging/MetricsLogger;-><init>()V
 Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
 Lcom/android/internal/net/VpnConfig;-><init>()V
@@ -1981,6 +2225,7 @@
 Lcom/android/internal/R$styleable;->MenuGroup:[I
 Lcom/android/internal/R$styleable;->MenuItem:[I
 Lcom/android/internal/R$styleable;->MenuView:[I
+Lcom/android/internal/R$styleable;->NumberPicker:[I
 Lcom/android/internal/R$styleable;->PopupWindow:[I
 Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I
 Lcom/android/internal/R$styleable;->PopupWindow_popupBackground:I
@@ -2106,6 +2351,7 @@
 Lcom/android/internal/R$styleable;->TextView_typeface:I
 Lcom/android/internal/R$styleable;->TextView_width:I
 Lcom/android/internal/R$styleable;->Theme:[I
+Lcom/android/internal/R$styleable;->TwoLineListItem:[I
 Lcom/android/internal/R$styleable;->View:[I
 Lcom/android/internal/R$styleable;->ViewAnimator:[I
 Lcom/android/internal/R$styleable;->ViewFlipper:[I
@@ -3126,6 +3372,7 @@
 Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
 Lcom/android/internal/telephony/ITelephony;->getCallState()I
 Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
+Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
 Lcom/android/internal/telephony/ITelephony;->getDataState()I
 Lcom/android/internal/telephony/ITelephony;->getNetworkType()I
 Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
deleted file mode 100644
index a21fa30..0000000
--- a/config/hiddenapi-vendor-list.txt
+++ /dev/null
@@ -1,247 +0,0 @@
-Landroid/app/IActivityController$Stub;-><init>()V
-Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
-Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
-Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
-Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
-Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
-Landroid/app/IActivityManager;->getLockTaskModeState()I
-Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
-Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
-Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
-Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
-Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
-Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
-Landroid/app/IAlarmManager;->setTime(J)Z
-Landroid/app/IAssistDataReceiver$Stub;-><init>()V
-Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
-Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
-Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
-Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
-Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
-Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
-Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
-Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
-Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
-Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
-Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
-Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
-Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
-Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
-Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
-Landroid/location/INetInitiatedListener$Stub;-><init>()V
-Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
-Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
-Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
-Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
-Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
-Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
-Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
-Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
-Landroid/net/InterfaceConfiguration;-><init>()V
-Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
-Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
-Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
-Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
-Landroid/os/AsyncResult;->result:Ljava/lang/Object;
-Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
-Landroid/os/BatteryStats$HistoryItem;-><init>()V
-Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
-Landroid/os/BatteryStats$HistoryItem;->cmd:B
-Landroid/os/BatteryStats$HistoryItem;->states:I
-Landroid/os/BatteryStats$HistoryItem;->time:J
-Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
-Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
-Landroid/os/BatteryStats$Uid;-><init>()V
-Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
-Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
-Landroid/os/Broadcaster;-><init>()V
-Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
-Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
-Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
-Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
-Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
-Landroid/os/Handler;->getMain()Landroid/os/Handler;
-Landroid/os/HwBinder;->reportSyspropChanged()V
-Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
-Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
-Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
-Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
-Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
-Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/IPowerManager;->goToSleep(JII)V
-Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
-Landroid/os/IRemoteCallback$Stub;-><init>()V
-Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
-Landroid/os/Parcel;->readBlob()[B
-Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
-Landroid/os/Parcel;->writeBlob([B)V
-Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/Registrant;->clear()V
-Landroid/os/Registrant;->notifyRegistrant()V
-Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;-><init>()V
-Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
-Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/RegistrantList;->notifyRegistrants()V
-Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
-Landroid/os/RegistrantList;->removeCleared()V
-Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
-Landroid/os/ServiceSpecificException;->errorCode:I
-Landroid/os/storage/StorageEventListener;-><init>()V
-Landroid/os/SystemProperties;->reportSyspropChanged()V
-Landroid/os/SystemService;->start(Ljava/lang/String;)V
-Landroid/os/SystemService;->stop(Ljava/lang/String;)V
-Landroid/os/SystemVibrator;-><init>()V
-Landroid/os/UserHandle;->isSameApp(II)Z
-Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
-Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
-Landroid/telephony/ims/compat/ImsService;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
-Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
-Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
-Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
-Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
-Landroid/view/IRecentsAnimationController;->finish(Z)V
-Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
-Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
-Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
-Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
-Landroid/view/IWindowManager;->endProlongedAnimations()V
-Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
-Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
-Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
-Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
-Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
-Lcom/android/internal/content/PackageMonitor;-><init>()V
-Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
-Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
-Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
-Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
-Lcom/android/internal/R$styleable;->NumberPicker:[I
-Lcom/android/internal/R$styleable;->TwoLineListItem:[I
-Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6f0b6c8..c7a9d99 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1731,8 +1731,11 @@
             throw new IllegalArgumentException("connection is null");
         }
         if (mPackageInfo != null) {
-            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
-                    getOuterContext(), conn);
+            IServiceConnection sd = mPackageInfo.lookupServiceDispatcher(conn, getOuterContext());
+            if (sd == null) {
+                throw new IllegalArgumentException("ServiceConnection not currently bound: "
+                        + conn);
+            }
             try {
                 ActivityManager.getService().updateServiceGroup(sd, group, importance);
             } catch (RemoteException e) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3f10754..759763b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -696,6 +696,23 @@
         return loaders;
     }
 
+    private StrictMode.ThreadPolicy allowThreadDiskReads() {
+        if (mActivityThread == null) {
+            // When LoadedApk is used without an ActivityThread (usually in a
+            // zygote context), don't call into StrictMode, as it initializes
+            // the binder subsystem, which we don't want.
+            return null;
+        }
+
+        return StrictMode.allowThreadDiskReads();
+    }
+
+    private void setThreadPolicy(StrictMode.ThreadPolicy policy) {
+        if (mActivityThread != null && policy != null) {
+            StrictMode.setThreadPolicy(policy);
+        }
+    }
+
     private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
         if (mPackageName.equals("android")) {
             // Note: This branch is taken for system server and we don't need to setup
@@ -718,8 +735,11 @@
 
         // Avoid the binder call when the package is the current application package.
         // The activity manager will perform ensure that dexopt is performed before
-        // spinning up the process.
-        if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) {
+        // spinning up the process. Similarly, don't call into binder when we don't
+        // have an ActivityThread object.
+        if (mActivityThread != null
+                && !Objects.equals(mPackageName, ActivityThread.currentPackageName())
+                && mIncludeCode) {
             try {
                 ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
                         PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
@@ -790,12 +810,12 @@
         // mIncludeCode == false).
         if (!mIncludeCode) {
             if (mDefaultClassLoader == null) {
-                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+                StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
                 mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                         "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
                         librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                         null /* classLoaderName */);
-                StrictMode.setThreadPolicy(oldPolicy);
+                setThreadPolicy(oldPolicy);
                 mAppComponentFactory = AppComponentFactory.DEFAULT;
             }
 
@@ -822,7 +842,7 @@
         if (mDefaultClassLoader == null) {
             // Temporarily disable logging of disk reads on the Looper thread
             // as this is early and necessary.
-            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
 
             List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
                     mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
@@ -834,18 +854,18 @@
                     mApplicationInfo.classLoaderName, sharedLibraries);
             mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
 
-            StrictMode.setThreadPolicy(oldPolicy);
+            setThreadPolicy(oldPolicy);
             // Setup the class loader paths for profiling.
             needToSetupJitProfiles = true;
         }
 
         if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
             // Temporarily disable logging of disk reads on the Looper thread as this is necessary
-            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
             try {
                 ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths);
             } finally {
-                StrictMode.setThreadPolicy(oldPolicy);
+                setThreadPolicy(oldPolicy);
             }
         }
 
@@ -879,11 +899,11 @@
             extraLibPaths.add("/product/lib" + abiSuffix);
         }
         if (!extraLibPaths.isEmpty()) {
-            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
             try {
                 ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
             } finally {
-                StrictMode.setThreadPolicy(oldPolicy);
+                setThreadPolicy(oldPolicy);
             }
         }
 
@@ -1639,6 +1659,19 @@
         }
     }
 
+    @UnsupportedAppUsage
+    public IServiceConnection lookupServiceDispatcher(ServiceConnection c,
+            Context context) {
+        synchronized (mServices) {
+            LoadedApk.ServiceDispatcher sd = null;
+            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
+            if (map != null) {
+                sd = map.get(c);
+            }
+            return sd != null ? sd.getIServiceConnection() : null;
+        }
+    }
+
     public final IServiceConnection forgetServiceDispatcher(Context context,
             ServiceConnection c) {
         synchronized (mServices) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0281e6a..aa1b5af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8263,7 +8263,7 @@
                 customContent = customContent.clone();
                 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
                 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
-                remoteViews.setReapplyDisallowed();
+                remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
             }
             // also update the end margin if there is an image
             Resources resources = mBuilder.mContext.getResources();
@@ -8394,7 +8394,7 @@
                 customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
                 remoteViews.removeAllViews(id);
                 remoteViews.addView(id, customContent);
-                remoteViews.setReapplyDisallowed();
+                remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
             }
             return remoteViews;
         }
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 32fc0dc..9dcd122 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -119,6 +119,7 @@
     /**
      * @deprecated Use {@link #addConfig(long, byte[])}
      */
+    @Deprecated
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public boolean addConfiguration(long configKey, byte[] config) {
         try {
@@ -154,6 +155,7 @@
     /**
      * @deprecated Use {@link #removeConfig(long)}
      */
+    @Deprecated
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public boolean removeConfiguration(long configKey) {
         try {
@@ -222,6 +224,7 @@
     /**
      * @deprecated Use {@link #setBroadcastSubscriber(PendingIntent, long, long)}
      */
+    @Deprecated
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public boolean setBroadcastSubscriber(
             long configKey, long subscriberId, PendingIntent pendingIntent) {
@@ -275,6 +278,7 @@
     /**
      * @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)}
      */
+    @Deprecated
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
         try {
@@ -312,6 +316,7 @@
     /**
      * @deprecated Use {@link #getReports(long)}
      */
+    @Deprecated
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public @Nullable byte[] getData(long configKey) {
         try {
@@ -348,6 +353,7 @@
     /**
      * @deprecated Use {@link #getStatsMetadata()}
      */
+    @Deprecated
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public @Nullable byte[] getMetadata() {
         try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index dfe371c..0404e80 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -59,6 +59,7 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.camera2.CameraManager;
+import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.face.FaceManager;
 import android.hardware.face.IFaceService;
@@ -386,6 +387,14 @@
                 return new DisplayManager(ctx.getOuterContext());
             }});
 
+        registerService(Context.COLOR_DISPLAY_SERVICE, ColorDisplayManager.class,
+                new CachedServiceFetcher<ColorDisplayManager>() {
+                    @Override
+                    public ColorDisplayManager createService(ContextImpl ctx) {
+                        return new ColorDisplayManager();
+                    }
+                });
+
         // InputMethodManager has its own cache strategy based on display id to support apps that
         // still assume InputMethodManager is a per-process singleton and it's safe to directly
         // access internal fields via reflection.  Hence directly use ServiceFetcher instead of
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 78b7d48..257122d 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -75,6 +75,9 @@
     /** The current windowing mode of the configuration. */
     private @WindowingMode int mWindowingMode;
 
+    /** The display windowing mode of the configuration */
+    private @WindowingMode int mDisplayWindowingMode;
+
     /** Windowing mode is currently not defined. */
     public static final int WINDOWING_MODE_UNDEFINED = 0;
     /** Occupies the full area of the screen or the parent container. */
@@ -176,6 +179,10 @@
     /** Bit that indicates that the {@link #mRotation} changed.
      * @hide */
     public static final int WINDOW_CONFIG_ROTATION = 1 << 5;
+    /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
+     * @hide */
+    public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 6;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
             WINDOW_CONFIG_BOUNDS,
@@ -184,6 +191,7 @@
             WINDOW_CONFIG_ACTIVITY_TYPE,
             WINDOW_CONFIG_ALWAYS_ON_TOP,
             WINDOW_CONFIG_ROTATION,
+            WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
     })
     public @interface WindowConfig {}
 
@@ -211,6 +219,7 @@
         dest.writeInt(mActivityType);
         dest.writeInt(mAlwaysOnTop);
         dest.writeInt(mRotation);
+        dest.writeInt(mDisplayWindowingMode);
     }
 
     private void readFromParcel(Parcel source) {
@@ -220,6 +229,7 @@
         mActivityType = source.readInt();
         mAlwaysOnTop = source.readInt();
         mRotation = source.readInt();
+        mDisplayWindowingMode = source.readInt();
     }
 
     @Override
@@ -322,6 +332,12 @@
         return mWindowingMode;
     }
 
+    /** @hide */
+    public void setDisplayWindowingMode(@WindowingMode int windowingMode) {
+        mDisplayWindowingMode = windowingMode;
+    }
+
+
     public void setActivityType(@ActivityType int activityType) {
         if (mActivityType == activityType) {
             return;
@@ -351,6 +367,7 @@
         setActivityType(other.mActivityType);
         setAlwaysOnTop(other.mAlwaysOnTop);
         setRotation(other.mRotation);
+        setDisplayWindowingMode(other.mDisplayWindowingMode);
     }
 
     /** Set this object to completely undefined.
@@ -367,6 +384,7 @@
         setActivityType(ACTIVITY_TYPE_UNDEFINED);
         setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
         setRotation(ROTATION_UNDEFINED);
+        setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED);
     }
 
     /**
@@ -407,6 +425,11 @@
             changed |= WINDOW_CONFIG_ROTATION;
             setRotation(delta.mRotation);
         }
+        if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED
+                && mDisplayWindowingMode != delta.mDisplayWindowingMode) {
+            changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
+            setDisplayWindowingMode(delta.mDisplayWindowingMode);
+        }
         return changed;
     }
 
@@ -455,6 +478,11 @@
             changes |= WINDOW_CONFIG_ROTATION;
         }
 
+        if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED)
+                && mDisplayWindowingMode != other.mDisplayWindowingMode) {
+            changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
+        }
+
         return changes;
     }
 
@@ -493,6 +521,8 @@
         if (n != 0) return n;
         n = mRotation - that.mRotation;
         if (n != 0) return n;
+        n = mDisplayWindowingMode - that.mDisplayWindowingMode;
+        if (n != 0) return n;
 
         // if (n != 0) return n;
         return n;
@@ -522,6 +552,7 @@
         result = 31 * result + mActivityType;
         result = 31 * result + mAlwaysOnTop;
         result = 31 * result + mRotation;
+        result = 31 * result + mDisplayWindowingMode;
         return result;
     }
 
@@ -531,6 +562,7 @@
         return "{ mBounds=" + mBounds
                 + " mAppBounds=" + mAppBounds
                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
+                + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
                 + " mActivityType=" + activityTypeToString(mActivityType)
                 + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
                 + " mRotation=" + (mRotation == ROTATION_UNDEFINED
@@ -603,7 +635,8 @@
      * @hide
      */
     public boolean hasWindowDecorCaption() {
-        return mWindowingMode == WINDOWING_MODE_FREEFORM;
+        return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM
+                || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM);
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
new file mode 100644
index 0000000..f39a5f4
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -0,0 +1,209 @@
+/*
+ * 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.app.admin;
+
+import android.stats.devicepolicy.nano.StringList;
+import android.util.StatsLog;
+
+import com.android.framework.protobuf.nano.MessageNano;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+/**
+ * A wrapper for logging managed device events using {@link StatsLog}.
+ * <p/>
+ * This class allows chaining - each of its methods returns a reference to the current instance.
+ * <p/>
+ * Example usage:
+ * <code><pre>
+ * import android.stats.devicepolicy.DevicePolicyEnums;
+ *
+ * DevicePolicyEventLogger
+ *     .createEvent(DevicePolicyEnums.USER_RESTRICTION_CHANGED)
+ *     .setAdminPackageName(who)
+ *     .setString(key)
+ *     .setBoolean(enabledFromThisOwner)
+ *     .write();
+ * </pre></code>
+ *
+ * @see StatsLog
+ * @hide
+ */
+public final class DevicePolicyEventLogger {
+    private final int mEventId;
+    private int mIntValue;
+    private boolean mBooleanValue;
+    private long mTimePeriodMs;
+    private String[] mStringArrayValue;
+    private String mAdminPackageName;
+
+    private DevicePolicyEventLogger(int eventId) {
+        mEventId = eventId;
+    }
+
+    /**
+     * Creates a new {@link DevicePolicyEventLogger} instance for the specified
+     * <code>eventId</code>.
+     *
+     * @param eventId one of {@link android.stats.devicepolicy.DevicePolicyEnums} as defined in
+     * <code>core/proto/android/stats/devicepolicy/device_policy_enums.proto</code>
+     */
+    public static DevicePolicyEventLogger createEvent(int eventId) {
+        return new DevicePolicyEventLogger(eventId);
+    }
+
+    /**
+     * Returns the event id.
+     */
+    @VisibleForTesting
+    public int getEventId() {
+        return mEventId;
+    }
+
+    /**
+     * Sets a generic <code>int</code> value.
+     */
+    public DevicePolicyEventLogger setInt(int value) {
+        mIntValue = value;
+        return this;
+    }
+
+    /**
+     * Returns the generic <code>int</code> value.
+     */
+    @VisibleForTesting
+    public int getInt() {
+        return mIntValue;
+    }
+
+    /**
+     * Sets a generic <code>boolean</code> value.
+     */
+    public DevicePolicyEventLogger setBoolean(boolean value) {
+        mBooleanValue = value;
+        return this;
+    }
+
+    /**
+     * Returns the generic <code>boolean</code> value.
+     */
+    @VisibleForTesting
+    public boolean getBoolean() {
+        return mBooleanValue;
+    }
+
+    /**
+     * Sets a time period in milliseconds.
+     */
+    public DevicePolicyEventLogger setTimePeriod(long timePeriodMillis) {
+        mTimePeriodMs = timePeriodMillis;
+        return this;
+    }
+
+    /**
+     * Returns the time period in milliseconds.
+     */
+    @VisibleForTesting
+    public long getTimePeriod() {
+        return mTimePeriodMs;
+    }
+
+    /**
+     * Sets generic <code>String</code> values.
+     */
+    public DevicePolicyEventLogger setStrings(String... values) {
+        mStringArrayValue = values;
+        return this;
+    }
+
+    /**
+     * Sets generic <code>String</code> values.
+     * <p/>
+     * {@link #write()} logs the concatenation of <code>value</code> and <code>values</code>,
+     * in that order.
+     */
+    public DevicePolicyEventLogger setStrings(String value, String[] values) {
+        Preconditions.checkNotNull(values, "values parameter cannot be null");
+        mStringArrayValue = new String[values.length + 1];
+        mStringArrayValue[0] = value;
+        System.arraycopy(values, 0, mStringArrayValue, 1, values.length);
+        return this;
+    }
+
+    /**
+     * Sets generic <code>String</code> values.
+     * <p/>
+     * {@link #write()} logs the concatenation of <code>value1</code>, <code>value2</code>
+     * and <code>values</code>, in that order.
+     */
+    public DevicePolicyEventLogger setStrings(String value1, String value2, String[] values) {
+        Preconditions.checkNotNull(values, "values parameter cannot be null");
+        mStringArrayValue = new String[values.length + 2];
+        mStringArrayValue[0] = value1;
+        mStringArrayValue[1] = value2;
+        System.arraycopy(values, 0, mStringArrayValue, 2, values.length);
+        return this;
+    }
+
+    /**
+     * Returns the generic <code>String[]</code> value.
+     */
+    @VisibleForTesting
+    public String[] getStringArray() {
+        return mStringArrayValue;
+    }
+
+    /**
+     * Sets the package name of the admin application.
+     */
+    public DevicePolicyEventLogger setAdminPackageName(String packageName) {
+        mAdminPackageName = packageName;
+        return this;
+    }
+
+    /**
+     * Returns the package name of the admin application.
+     */
+    @VisibleForTesting
+    public String getAdminPackageName() {
+        return mAdminPackageName;
+    }
+
+    /**
+     * Writes the metric to {@link StatsLog}.
+     */
+    public void write() {
+        byte[] bytes = stringArrayValueToBytes(mStringArrayValue);
+        StatsLog.write(StatsLog.DEVICE_POLICY_EVENT, mEventId, mAdminPackageName, mIntValue,
+                mBooleanValue, mTimePeriodMs, bytes);
+    }
+
+    /**
+     * Converts the <code>String[] array</code> to <code>byte[]</code>.
+     * <p/>
+     * We can't log <code>String[]</code> using {@link StatsLog}. The convention is to assign
+     * the array to a proto object and convert it to <code>byte[]</code>.
+     */
+    private static byte[] stringArrayValueToBytes(String[] array) {
+        if (array == null) {
+            return null;
+        }
+        StringList stringList = new StringList();
+        stringList.stringValue = array;
+        return MessageNano.toByteArray(stringList);
+    }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 98d2a40..3a97284 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10092,11 +10092,40 @@
         if (mService != null) {
             try {
                 return mService.isPackageAllowedToAccessCalendarForUser(packageName,
-                        mContext.getUserId());
+                        myUserId());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
         return false;
     }
+
+    /**
+     * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+     *
+     * <p>To query for a specific user, use
+     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
+     * that user, and get a {@link DevicePolicyManager} from this context.
+     *
+     * @return the set of names of packages that were previously whitelisted via
+     * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+     * empty set if none have been whitelisted.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     * @hide
+     */
+    public @NonNull Set<String>  getCrossProfileCalendarPackages() {
+        throwIfParentInstance("getCrossProfileCalendarPackages");
+        if (mService != null) {
+            try {
+                return new ArraySet<>(mService.getCrossProfileCalendarPackagesForUser(
+                        myUserId()));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return Collections.emptySet();
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 297676d..fcf74ee 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -427,4 +427,5 @@
     boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
     List<String> getCrossProfileCalendarPackages(in ComponentName admin);
     boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
+    List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
 }
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 318dbee..c740c42 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -88,6 +88,7 @@
     int mViewMode = VIEW_MODE_NOINIT;
     int mLayoutId = -1;
     private OnClickHandler mOnClickHandler;
+    private boolean mOnLightBackground;
 
     private Executor mAsyncExecutor;
     private CancellationSignal mLastExecutionSignal;
@@ -374,6 +375,15 @@
     }
 
     /**
+     * Sets whether the widget should is being displayed on a light/white background and use an
+     * alternate UI if available.
+     * @see RemoteViews#setLightBackgroundLayoutId(int)
+     */
+    public void setOnLightBackground(boolean useDarkTextLayout) {
+        mOnLightBackground = useDarkTextLayout;
+    }
+
+    /**
      * Update the AppWidgetProviderInfo for this view, and reset it to the
      * initial layout.
      */
@@ -413,6 +423,10 @@
             mLayoutId = -1;
             mViewMode = VIEW_MODE_DEFAULT;
         } else {
+            if (mOnLightBackground) {
+                remoteViews = remoteViews.getDarkTextViews();
+            }
+
             if (mAsyncExecutor != null && useAsyncIfPossible) {
                 inflateAsync(remoteViews);
                 return;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 145c9273..d1017c5 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -51,6 +51,7 @@
 import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -586,7 +587,13 @@
         private int noteProxyOp(String callingPkg, int op) {
             if (op != AppOpsManager.OP_NONE) {
                 int mode = mAppOpsManager.noteProxyOp(op, callingPkg);
-                return mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode;
+                int nonDefaultMode = mode == MODE_DEFAULT ? interpretDefaultAppOpMode(op) : mode;
+                if (mode == MODE_DEFAULT && nonDefaultMode == MODE_IGNORED) {
+                    Slog.w(TAG, "Denying access for " + callingPkg + " to " + getClass().getName()
+                            + " (" + AppOpsManager.opToName(op)
+                            + " = " + AppOpsManager.opToName(mode) + ")");
+                }
+                return mode == MODE_DEFAULT ? nonDefaultMode : mode;
             }
 
             return AppOpsManager.MODE_ALLOWED;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 68aac64..e2c7b85 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3003,6 +3003,11 @@
      * how the process will be managed in some cases based on those flags.  Currently only
      * works on isolated processes (will be ignored for non-isolated processes).
      *
+     * <p>Note that this call does not take immediate effect, but will be applied the next
+     * time the impacted process is adjusted for some other reason.  Typically you would
+     * call this before then calling a new {@link #bindIsolatedService} on the service
+     * of interest, with that binding causing the process to be shuffled accordingly.</p>
+     *
      * @param conn The connection interface previously supplied to bindService().  This
      *             parameter must not be null.
      * @param group A group to put this connection's process in.  Upon calling here, this
@@ -3126,6 +3131,7 @@
             //@hide: HDMI_CONTROL_SERVICE,
             INPUT_SERVICE,
             DISPLAY_SERVICE,
+            //@hide COLOR_DISPLAY_SERVICE,
             USER_SERVICE,
             RESTRICTIONS_SERVICE,
             APP_OPS_SERVICE,
@@ -4108,6 +4114,16 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.display.ColorDisplayManager} for controlling color transforms.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.display.ColorDisplayManager
+     * @hide
+     */
+    public static final String COLOR_DISPLAY_SERVICE = "color_display";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.os.UserManager} for managing users on devices that support multiple users.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index edfb3a7..c2907d2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1916,12 +1916,15 @@
     /**
      * Activity action: Launch UI to review app uses of permissions.
      * <p>
-     * Input: Nothing
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission name
+     * that will be displayed by the launched UI.
      * </p>
      * <p>
      * Output: Nothing.
      * </p>
      *
+     * @see #EXTRA_PERMISSION_NAME
+     *
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 98a135f..07d6e47 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1303,7 +1303,7 @@
     /** {@hide} */
     public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) {
         long token = proto.start(fieldId);
-        super.writeToProto(proto, ApplicationInfoProto.PACKAGE);
+        super.writeToProto(proto, ApplicationInfoProto.PACKAGE, dumpFlags);
         proto.write(ApplicationInfoProto.PERMISSION, permission);
         proto.write(ApplicationInfoProto.PROCESS_NAME, processName);
         proto.write(ApplicationInfoProto.UID, uid);
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index cdb7814..ff7b347 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -433,18 +433,18 @@
     /**
      * @hide
      */
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId, int dumpFlags) {
         long token = proto.start(fieldId);
         if (name != null) {
             proto.write(PackageItemInfoProto.NAME, name);
         }
         proto.write(PackageItemInfoProto.PACKAGE_NAME, packageName);
-        if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) {
-            proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+        proto.write(PackageItemInfoProto.LABEL_RES, labelRes);
+        if (nonLocalizedLabel != null) {
             proto.write(PackageItemInfoProto.NON_LOCALIZED_LABEL, nonLocalizedLabel.toString());
-            proto.write(PackageItemInfoProto.ICON, icon);
-            proto.write(PackageItemInfoProto.BANNER, banner);
         }
+        proto.write(PackageItemInfoProto.ICON, icon);
+        proto.write(PackageItemInfoProto.BANNER, banner);
         proto.end(token);
     }
 
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 79bc9a3..73addb7 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -16,13 +16,15 @@
 
 package android.content.pm.permission;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -32,7 +34,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -109,13 +111,11 @@
      */
     public void getAppPermissions(@NonNull String packageName,
             @NonNull OnResultCallback callback, @Nullable Handler handler) {
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = packageName;
-        args.arg2 = callback;
-        args.arg3 = handler;
-        Message message = mRemoteService.obtainMessage(
-                RemoteService.MSG_GET_APP_PERMISSIONS, args);
-        mRemoteService.processMessage(message);
+        checkNotNull(packageName);
+        checkNotNull(callback);
+
+        mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
+                mRemoteService, packageName, callback, handler));
     }
 
     /**
@@ -124,24 +124,20 @@
      * @param packageName The package for which to revoke
      * @param permissionName The permission to revoke
      */
-    public void revokeRuntimePermission(String packageName, String permissionName) {
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = packageName;
-        args.arg2 = permissionName;
+    public void revokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permissionName) {
+        checkNotNull(packageName);
+        checkNotNull(permissionName);
 
-        Message message = mRemoteService.obtainMessage(
-                RemoteService.MSG_REVOKE_APP_PERMISSIONS, args);
-        mRemoteService.processMessage(message);
+        mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
+                mRemoteService, packageName, permissionName));
     }
 
     private static final class RemoteService
             extends Handler implements ServiceConnection {
         private static final long UNBIND_TIMEOUT_MILLIS = 10000;
 
-        public static final int MSG_GET_APP_PERMISSIONS = 1;
-        public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
-        public static final int MSG_UNBIND = 3;
-        public static final int MSG_REVOKE_APP_PERMISSIONS = 4;
+        public static final int MSG_UNBIND = 0;
 
         private final Object mLock = new Object();
 
@@ -191,82 +187,57 @@
             }
         }
 
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_GET_APP_PERMISSIONS: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    final String packageName = (String) args.arg1;
-                    final OnResultCallback callback = (OnResultCallback) args.arg2;
-                    final Handler handler = (Handler) args.arg3;
-                    args.recycle();
-                    final IRuntimePermissionPresenter remoteInstance;
-                    synchronized (mLock) {
-                        remoteInstance = mRemoteInstance;
-                    }
-                    if (remoteInstance == null) {
-                        return;
-                    }
-                    try {
-                        remoteInstance.getAppPermissions(packageName,
-                                new RemoteCallback(new RemoteCallback.OnResultListener() {
-                            @Override
-                            public void onResult(Bundle result) {
-                                final List<RuntimePermissionPresentationInfo> reportedPermissions;
-                                List<RuntimePermissionPresentationInfo> permissions = null;
-                                if (result != null) {
-                                    permissions = result.getParcelableArrayList(KEY_RESULT);
-                                }
-                                if (permissions == null) {
-                                    permissions = Collections.emptyList();
-                                }
-                                reportedPermissions = permissions;
-                                if (handler != null) {
-                                    handler.post(new Runnable() {
-                                        @Override
-                                        public void run() {
-                                            callback.onGetAppPermissions(reportedPermissions);
-                                        }
-                                    });
-                                } else {
-                                    callback.onGetAppPermissions(reportedPermissions);
-                                }
+        private void getAppPermissions(@NonNull String packageName,
+                @NonNull OnResultCallback callback, @Nullable Handler handler) {
+            final IRuntimePermissionPresenter remoteInstance;
+            synchronized (mLock) {
+                remoteInstance = mRemoteInstance;
+            }
+            if (remoteInstance == null) {
+                return;
+            }
+            try {
+                remoteInstance.getAppPermissions(packageName,
+                        new RemoteCallback(result -> {
+                            final List<RuntimePermissionPresentationInfo> reportedPermissions;
+                            List<RuntimePermissionPresentationInfo> permissions = null;
+                            if (result != null) {
+                                permissions = result.getParcelableArrayList(KEY_RESULT);
+                            }
+                            if (permissions == null) {
+                                permissions = Collections.emptyList();
+                            }
+                            reportedPermissions = permissions;
+                            if (handler != null) {
+                                handler.post(
+                                        () -> callback.onGetAppPermissions(reportedPermissions));
+                            } else {
+                                callback.onGetAppPermissions(reportedPermissions);
                             }
                         }, this));
-                    } catch (RemoteException re) {
-                        Log.e(TAG, "Error getting app permissions", re);
-                    }
-                    scheduleUnbind();
-                } break;
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error getting app permissions", re);
+            }
+            scheduleUnbind();
 
-                case MSG_UNBIND: {
-                    synchronized (mLock) {
-                        if (mBound) {
-                            mContext.unbindService(this);
-                            mBound = false;
-                        }
-                        mRemoteInstance = null;
-                    }
-                } break;
+            synchronized (mLock) {
+                scheduleNextMessageIfNeededLocked();
+            }
+        }
 
-                case MSG_REVOKE_APP_PERMISSIONS: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    final String packageName = (String) args.arg1;
-                    final String permissionName = (String) args.arg2;
-                    args.recycle();
-                    final IRuntimePermissionPresenter remoteInstance;
-                    synchronized (mLock) {
-                        remoteInstance = mRemoteInstance;
-                    }
-                    if (remoteInstance == null) {
-                        return;
-                    }
-                    try {
-                        remoteInstance.revokeRuntimePermission(packageName, permissionName);
-                    } catch (RemoteException re) {
-                        Log.e(TAG, "Error getting app permissions", re);
-                    }
-                } break;
+        private void revokeAppPermissions(@NonNull String packageName,
+                @NonNull String permissionName) {
+            final IRuntimePermissionPresenter remoteInstance;
+            synchronized (mLock) {
+                remoteInstance = mRemoteInstance;
+            }
+            if (remoteInstance == null) {
+                return;
+            }
+            try {
+                remoteInstance.revokeRuntimePermission(packageName, permissionName);
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error getting app permissions", re);
             }
 
             synchronized (mLock) {
@@ -274,6 +245,16 @@
             }
         }
 
+        private void unbind() {
+            synchronized (mLock) {
+                if (mBound) {
+                    mContext.unbindService(this);
+                    mBound = false;
+                }
+                mRemoteInstance = null;
+            }
+        }
+
         @GuardedBy("mLock")
         private void scheduleNextMessageIfNeededLocked() {
             if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
@@ -284,7 +265,8 @@
 
         private void scheduleUnbind() {
             removeMessages(MSG_UNBIND);
-            sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS);
+            sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
+                    .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
         }
     }
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7148b12..5e402c7 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -393,7 +393,7 @@
      *         doesn't have any recommendation for this use case or the recommended configurations
      *         are invalid.
      */
-    public RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
+    public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
             @RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
         if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
                 (usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) ||
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index dc6cffc..448591f 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -968,6 +968,36 @@
     public abstract void close();
 
     /**
+     * Checks whether a particular {@link SessionConfiguration} is supported by the camera device.
+     *
+     * <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result
+     * confirms whether or not the passed session configuration can be successfully used to
+     * create a camera capture session using
+     * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
+     * </p>
+     *
+     * <p>The method can be called at any point before, during and after active capture session.
+     * It must not impact normal camera behavior in any way and must complete significantly
+     * faster than creating a regular or constrained capture session.</p>
+     *
+     * <p>Note that session parameters will be ignored and calls to
+     * {@link SessionConfiguration#setSessionParameters} are not required.</p>
+     *
+     * @return {@code true} if the given session configuration is supported by the camera device
+     *         {@code false} otherwise.
+     * @throws UnsupportedOperationException if the query operation is not supported by the camera
+     *                                       device
+     * @throws IllegalArgumentException if the session configuration is invalid
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     */
+    public boolean isSessionConfigurationSupported(
+            @NonNull SessionConfiguration sessionConfig) throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * A callback objects for receiving updates about the state of a camera device.
      *
      * <p>A callback instance must be provided to the {@link CameraManager#openCamera} method to
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 7810e6c..57b608f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -703,6 +703,17 @@
         }
     }
 
+    @Override
+    public boolean isSessionConfigurationSupported(
+            @NonNull SessionConfiguration sessionConfig) throws CameraAccessException,
+            UnsupportedOperationException, IllegalArgumentException {
+        synchronized(mInterfaceLock) {
+            checkIfCameraClosedOrInError();
+
+            return mRemoteDevice.isSessionConfigurationSupported(sessionConfig);
+        }
+    }
+
     /**
      * For use by backwards-compatibility code only.
      */
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 1f4ed13..c8ded8d 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -30,9 +30,11 @@
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.view.Surface;
 
 /**
@@ -181,6 +183,25 @@
         }
     }
 
+    public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig)
+            throws CameraAccessException {
+        try {
+            return mRemoteDevice.isSessionConfigurationSupported(sessionConfig);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
+                throw new UnsupportedOperationException("Session configuration query not " +
+                        "supported");
+            } else if (e.errorCode == ICameraService.ERROR_ILLEGAL_ARGUMENT) {
+                throw new IllegalArgumentException("Invalid session configuration");
+            }
+
+            throw e;
+        } catch (Throwable t) {
+            CameraManager.throwAsPublicException(t);
+            throw new UnsupportedOperationException("Unexpected exception", t);
+        }
+    }
+
     public long flush() throws CameraAccessException {
         try {
             return mRemoteDevice.flush();
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index bc7b126..123eb8e 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -28,6 +28,7 @@
 import android.hardware.camera2.impl.CaptureResultExtras;
 import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.os.ConditionVariable;
 import android.os.IBinder;
@@ -480,6 +481,12 @@
     }
 
     @Override
+    public boolean isSessionConfigurationSupported(SessionConfiguration sessionConfig) {
+        // TODO: Add support for this in legacy mode
+        throw new UnsupportedOperationException("Session configuration query not supported!");
+    }
+
+    @Override
     public void beginConfigure() {
         if (DEBUG) {
             Log.d(TAG, "beginConfigure called.");
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 83a0228..1b28d61 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -521,9 +521,10 @@
         clearState();
     }
 
-    private void makeCurrent(EGLSurface surface) {
+    private void makeCurrent(EGLSurface surface)
+            throws LegacyExceptionUtils.BufferQueueAbandonedException {
         EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
-        checkEglError("makeCurrent");
+        checkEglDrawError("makeCurrent");
     }
 
     private boolean swapBuffers(EGLSurface surface)
@@ -557,6 +558,17 @@
         }
     }
 
+    private void checkEglDrawError(String msg)
+            throws LegacyExceptionUtils.BufferQueueAbandonedException {
+        int error;
+        if ((error = EGL14.eglGetError()) == EGL14.EGL_BAD_NATIVE_WINDOW) {
+            throw new LegacyExceptionUtils.BufferQueueAbandonedException();
+        }
+        if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
+            throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
+        }
+    }
+
     private void checkEglError(String msg) {
         int error;
         if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
@@ -709,8 +721,14 @@
         if (mConversionSurfaces.size() > 0) {
             configureEGLPbufferSurfaces(mConversionSurfaces);
         }
-        makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
+
+        try {
+            makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
                 mConversionSurfaces.get(0).eglSurface);
+        } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
+        }
+
         initializeGLState();
         mSurfaceTexture = new SurfaceTexture(getTextureId());
 
@@ -798,9 +816,9 @@
         }
         for (EGLSurfaceHolder holder : mConversionSurfaces) {
             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
-                makeCurrent(holder.eglSurface);
                 // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
                 try {
+                    makeCurrent(holder.eglSurface);
                     drawFrame(mSurfaceTexture, holder.width, holder.height,
                             (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
                                     FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 59e4a33..068c0ce 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2.params;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.ImageFormat;
@@ -173,7 +174,7 @@
      *
      * @return Use case id.
      */
-    public int getRecommendedUseCase() {
+    public @RecommendedUsecase int getRecommendedUseCase() {
         return mUsecase;
     }
 
@@ -326,7 +327,7 @@
      * @throws IllegalArgumentException if input size does not exist in the return value of
      *             getHighSpeedVideoSizes
      */
-    public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(Size size) {
+    public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(@NonNull Size size) {
         return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRangesFor(size));
     }
 
@@ -349,14 +350,14 @@
      * For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizesFor}.
      * </p>
      *
-     * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()}
+     * @param fpsRange one of the FPS ranges returned by {@link #getHighSpeedVideoFpsRanges()}
      * @return A non-modifiable set of video sizes to create high speed capture sessions for high
      *         speed streaming use cases.
      *
      * @throws IllegalArgumentException if input FPS range does not exist in the return value of
      *         getHighSpeedVideoFpsRanges
      */
-    public @Nullable Set<Size> getHighSpeedVideoSizesFor(Range<Integer> fpsRange) {
+    public @Nullable Set<Size> getHighSpeedVideoSizesFor(@NonNull Range<Integer> fpsRange) {
         return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizesFor(fpsRange));
     }
 
@@ -390,10 +391,8 @@
      *          0 if the minimum frame duration is not available.
      *
      * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} was {@code null}
-     *
      */
-    public long getOutputMinFrameDuration(int format, Size size) {
+    public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) {
         return mRecommendedMap.getOutputMinFrameDuration(format, size);
     }
 
@@ -409,9 +408,8 @@
      * @return a stall duration {@code >=} 0 in nanoseconds
      *
      * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} was {@code null}
      */
-    public long getOutputStallDuration(int format, Size size) {
+    public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) {
         return mRecommendedMap.getOutputStallDuration(format, size);
     }
 
@@ -422,16 +420,12 @@
      * </p>
      *
      * @param klass
-     *          a non-{@code null} {@link Class} object reference
+     *          a {@link Class} object reference
      * @return
      *          a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format,
      *          or {@code null} if the {@code klass} is not a supported output.
-     *
-     *
-     * @throws NullPointerException if {@code klass} was {@code null}
-     *
      */
-    public <T> @Nullable Set<Size> getOutputSizes(Class<T> klass) {
+    public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) {
         if (mSupportsPrivate) {
             return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass));
         }
@@ -447,16 +441,15 @@
      * {@link StreamConfigurationMap#getOutputMinFrameDuration(Class, Size)}.</p>
      *
      * @param klass
-     *          a class which  has a non-empty array returned by {@link #getOutputSizes(Class)}
+     *          a class which has a non-empty array returned by {@link #getOutputSizes(Class)}
      * @param size an output-compatible size
      * @return a minimum frame duration {@code >} 0 in nanoseconds, or
      *          0 if the minimum frame duration is not available.
      *
      * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
-     *
      */
-    public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
+    public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass,
+            @NonNull final Size size) {
         if (mSupportsPrivate) {
             return mRecommendedMap.getOutputMinFrameDuration(klass, size);
         }
@@ -473,14 +466,13 @@
      * @param klass
      *          a class which has a non-empty array returned by {@link #getOutputSizes(Class)}.
      * @param size an output-compatible size
-     * @return a minimum frame duration {@code >=} 0 in nanoseconds, or 0 if the stall duration is
+     * @return a minimum frame duration {@code >} 0 in nanoseconds, or 0 if the stall duration is
      *         not available.
      *
      * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
-     *
      */
-    public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+    public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass,
+            @NonNull final Size size) {
         if (mSupportsPrivate) {
             return mRecommendedMap.getOutputStallDuration(klass, size);
         }
@@ -495,14 +487,13 @@
      * <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}.
      * </p>
      *
-     * @param surface a non-{@code null} {@link Surface} object reference
+     * @param surface a {@link Surface} object reference
      * @return {@code true} if this is supported, {@code false} otherwise
      *
-     * @throws NullPointerException if {@code surface} was {@code null}
      * @throws IllegalArgumentException if the Surface endpoint is no longer valid
      *
      */
-    public boolean isOutputSupportedFor(Surface surface) {
+    public boolean isOutputSupportedFor(@NonNull Surface surface) {
         return mRecommendedMap.isOutputSupportedFor(surface);
     }
 
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 8a8afb2..3ea58ad 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -27,6 +27,10 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.utils.HashCodeHelpers;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
 
 import java.util.Collections;
 import java.util.List;
@@ -40,7 +44,9 @@
 /**
  * A helper class that aggregates all supported arguments for capture session initialization.
  */
-public final class SessionConfiguration {
+public final class SessionConfiguration implements Parcelable {
+    private static final String TAG = "SessionConfiguration";
+
     /**
      * A regular session type containing instances of {@link OutputConfiguration} running
      * at regular non high speed FPS ranges and optionally {@link InputConfiguration} for
@@ -110,6 +116,108 @@
     }
 
     /**
+     * Create a SessionConfiguration from Parcel.
+     * No support for parcelable 'mStateCallback', 'mExecutor' and 'mSessionParameters' yet.
+     */
+    private SessionConfiguration(@NonNull Parcel source) {
+        int sessionType = source.readInt();
+        int inputWidth = source.readInt();
+        int inputHeight = source.readInt();
+        int inputFormat = source.readInt();
+        ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>();
+        source.readTypedList(outConfigs, OutputConfiguration.CREATOR);
+
+        if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) {
+            mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat);
+        }
+        mSessionType = sessionType;
+        mOutputConfigurations = outConfigs;
+    }
+
+    public static final Parcelable.Creator<SessionConfiguration> CREATOR =
+            new Parcelable.Creator<SessionConfiguration> () {
+        @Override
+        public SessionConfiguration createFromParcel(Parcel source) {
+            try {
+                SessionConfiguration sessionConfiguration = new SessionConfiguration(source);
+                return sessionConfiguration;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating SessionConfiguration from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public SessionConfiguration[] newArray(int size) {
+            return new SessionConfiguration[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("dest must not be null");
+        }
+        dest.writeInt(mSessionType);
+        if (mInputConfig != null) {
+            dest.writeInt(mInputConfig.getWidth());
+            dest.writeInt(mInputConfig.getHeight());
+            dest.writeInt(mInputConfig.getFormat());
+        } else {
+            dest.writeInt(/*inputWidth*/ 0);
+            dest.writeInt(/*inputHeight*/ 0);
+            dest.writeInt(/*inputFormat*/ -1);
+        }
+        dest.writeTypedList(mOutputConfigurations);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Check if this {@link SessionConfiguration} is equal to another {@link SessionConfiguration}.
+     *
+     * <p>Two output session configurations are only equal if and only if the underlying input
+     * configuration, output configurations, and session type are equal. </p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        } else if (this == obj) {
+            return true;
+        } else if (obj instanceof SessionConfiguration) {
+            final SessionConfiguration other = (SessionConfiguration) obj;
+            if (mInputConfig != other.mInputConfig || mSessionType != other.mSessionType ||
+                    mOutputConfigurations.size() != other.mOutputConfigurations.size()) {
+                return false;
+            }
+
+            for (int i = 0;  i < mOutputConfigurations.size(); i++) {
+                if (!mOutputConfigurations.get(i).equals(other.mOutputConfigurations.get(i)))
+                    return false;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(), mInputConfig.hashCode(),
+                mSessionType);
+    }
+
+    /**
      * Retrieve the type of the capture session.
      *
      * @return The capture session type.
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index 0a76c2b..a4c1332 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -16,20 +16,81 @@
 
 package android.hardware.display;
 
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
 import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
 
 import com.android.internal.R;
 
 /**
  * Manages the display's color transforms and modes.
+ *
  * @hide
  */
+@SystemService(Context.COLOR_DISPLAY_SERVICE)
 public final class ColorDisplayManager {
 
+    private final ColorDisplayManagerInternal mManager;
+
+    /**
+     * @hide
+     */
+    public ColorDisplayManager() {
+        mManager = ColorDisplayManagerInternal.getInstance();
+    }
+
+    /**
+     * Returns whether the device has a wide color gamut display.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean isDeviceColorManaged() {
+        return mManager.isDeviceColorManaged();
+    }
+
     /**
      * Returns {@code true} if Night Display is supported by the device.
      */
     public static boolean isNightDisplayAvailable(Context context) {
         return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
     }
+
+    private static class ColorDisplayManagerInternal {
+
+        private static ColorDisplayManagerInternal sInstance;
+
+        private final IColorDisplayManager mCdm;
+
+        private ColorDisplayManagerInternal(IColorDisplayManager colorDisplayManager) {
+            mCdm = colorDisplayManager;
+        }
+
+        public static ColorDisplayManagerInternal getInstance() {
+            synchronized (ColorDisplayManagerInternal.class) {
+                if (sInstance == null) {
+                    try {
+                        IBinder b = ServiceManager.getServiceOrThrow(Context.COLOR_DISPLAY_SERVICE);
+                        sInstance = new ColorDisplayManagerInternal(
+                                IColorDisplayManager.Stub.asInterface(b));
+                    } catch (ServiceNotFoundException e) {
+                        throw new IllegalStateException(e);
+                    }
+                }
+                return sInstance;
+            }
+        }
+
+        boolean isDeviceColorManaged() {
+            try {
+                return mCdm.isDeviceColorManaged();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
new file mode 100644
index 0000000..f786589
--- /dev/null
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.hardware.display;
+
+/** @hide */
+interface IColorDisplayManager {
+    boolean isDeviceColorManaged();
+}
\ No newline at end of file
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 64314a7..2ae796c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -85,6 +85,15 @@
     public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE
 
     /**
+     * Value to represents that a calling work source is not set.
+     *
+     * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
+     *
+     * @hide
+     */
+    public static final int UNSET_WORKSOURCE = -1;
+
+    /**
      * Control whether dump() calls are allowed.
      */
     private static volatile String sDumpDisabled = null;
@@ -449,8 +458,6 @@
      * }
      * </pre>
      *
-     * <p>The work source will be propagated for future outgoing binder transactions
-     * executed on this thread.
      * @hide
      **/
     @CriticalNative
@@ -912,6 +919,16 @@
     // Entry point from android_util_Binder.cpp's onTransact
     private boolean execTransact(int code, long dataObj, long replyObj,
             int flags) {
+        final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
+        try {
+            return execTransactInternal(code, dataObj, replyObj, flags);
+        } finally {
+            ThreadLocalWorkSource.restore(origWorkSource);
+        }
+    }
+
+    private boolean execTransactInternal(int code, long dataObj, long replyObj,
+            int flags) {
         // Make sure the observer won't change while processing a transaction.
         final BinderInternal.Observer observer = sObserver;
         final CallSession callSession =
@@ -925,7 +942,6 @@
         // Log any exceptions as warnings, don't silently suppress them.
         // If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
         final boolean tracingEnabled = Binder.isTracingEnabled();
-        final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
         try {
             if (tracingEnabled) {
                 final String transactionName = getTransactionName(code);
@@ -952,7 +968,6 @@
             }
             res = true;
         } finally {
-            ThreadLocalWorkSource.restore(origWorkSource);
             if (tracingEnabled) {
                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
             }
@@ -972,7 +987,6 @@
         if (observer != null) {
             observer.callEnded(callSession, requestSizeBytes, replySizeBytes);
         }
-
         return res;
     }
 }
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 900b62d..8cafbde 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -269,7 +269,7 @@
         }
 
         // If no temp rules, load the real ones from the APK
-        if (rulesFd == null) {
+        if (DEBUG && (rulesFd == null)) {
 
             // Pass the rules file to loader for ANGLE decisions
             AssetManager angleAssets = null;
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8160338..9280cb9 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -67,7 +67,7 @@
 
     /**
       * Register a listener for thermal status change.
-      * @param listener the IThermalStatusListener to be notified.
+      * @param listener the {@link android.os.IThermalStatusListener} to be notified.
       * @return true if registered successfully.
       * {@hide}
       */
@@ -75,7 +75,7 @@
 
     /**
       * Unregister a previously-registered listener for thermal status.
-      * @param listener the IThermalStatusListener to no longer be notified.
+      * @param listener the {@link android.os.IThermalStatusListener} to no longer be notified.
       * @return true if unregistered successfully.
       * {@hide}
       */
@@ -86,5 +86,5 @@
       * @return status defined in {@link android.os.Temperature}.
       * {@hide}
       */
-    int getCurrentStatus();
+    int getCurrentThermalStatus();
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 894015f..c3e0489 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -17,7 +17,9 @@
 package android.os;
 
 import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
@@ -25,11 +27,15 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.service.dreams.Sandman;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * This class gives you control of the power state of the device.
@@ -643,6 +649,9 @@
     final IPowerManager mService;
     final Handler mHandler;
 
+    IThermalService mThermalService;
+    private ArrayMap<ThermalStatusCallback, IThermalStatusListener> mCallbackMap = new ArrayMap<>();
+
     IDeviceIdleController mIDeviceIdleController;
 
     /**
@@ -1443,6 +1452,159 @@
     }
 
     /**
+     * Thermal status code: Not under throttling.
+     */
+    public static final int THERMAL_STATUS_NONE = Temperature.THROTTLING_NONE;
+
+    /**
+     * Thermal status code: Light throttling where UX is not impacted.
+     */
+    public static final int THERMAL_STATUS_LIGHT = Temperature.THROTTLING_LIGHT;
+
+    /**
+     * Thermal status code: Moderate throttling where UX is not largely impacted.
+     */
+    public static final int THERMAL_STATUS_MODERATE = Temperature.THROTTLING_MODERATE;
+
+    /**
+     * Thermal status code: Severe throttling where UX is largely impacted.
+     */
+    public static final int THERMAL_STATUS_SEVERE = Temperature.THROTTLING_SEVERE;
+
+    /**
+     * Thermal status code: Platform has done everything to reduce power.
+     */
+    public static final int THERMAL_STATUS_CRITICAL = Temperature.THROTTLING_CRITICAL;
+
+    /**
+     * Thermal status code: Key components in platform are shutting down due to thermal condition.
+     * Device functionalities will be limited.
+     */
+    public static final int THERMAL_STATUS_EMERGENCY = Temperature.THROTTLING_EMERGENCY;
+
+    /**
+     * Thermal status code: Need shutdown immediately.
+     */
+    public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN;
+
+    /** @hide */
+    @IntDef(prefix = { "THERMAL_STATUS_" }, value = {
+            THERMAL_STATUS_NONE,
+            THERMAL_STATUS_LIGHT,
+            THERMAL_STATUS_MODERATE,
+            THERMAL_STATUS_SEVERE,
+            THERMAL_STATUS_CRITICAL,
+            THERMAL_STATUS_EMERGENCY,
+            THERMAL_STATUS_SHUTDOWN,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThermalStatus {}
+
+    /**
+     * This function returns the current thermal status of the device.
+     *
+     * @return thermal status as int, {@link #THERMAL_STATUS_NONE} if device in not under
+     * thermal throttling.
+     */
+    public @ThermalStatus int getCurrentThermalStatus() {
+        synchronized (this) {
+            if (mThermalService == null) {
+                mThermalService = IThermalService.Stub.asInterface(
+                        ServiceManager.getService(Context.THERMAL_SERVICE));
+            }
+            try {
+                return mThermalService.getCurrentThermalStatus();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+    }
+
+    /**
+     * Callback passed to
+     * {@link PowerManager#registerThermalStatusCallback} and
+     * {@link PowerManager#unregisterThermalStatusCallback}
+     * to notify caller of thermal status.
+     */
+    public abstract static class ThermalStatusCallback {
+
+        /**
+         * Called when overall thermal throttling status changed.
+         * @param status defined in {@link android.os.Temperature}.
+         */
+        public void onStatusChange(@ThermalStatus int status) {}
+    }
+
+    /**
+     * This function registers a callback for thermal status change.
+     *
+     * @param callback callback to be registered.
+     * @param executor {@link Executor} to handle the callbacks.
+     */
+    public void registerThermalStatusCallback(
+            @NonNull ThermalStatusCallback callback, @NonNull @CallbackExecutor Executor executor) {
+        Preconditions.checkNotNull(callback, "callback cannnot be null");
+        Preconditions.checkNotNull(executor, "executor cannnot be null");
+        synchronized (this) {
+            if (mThermalService == null) {
+                mThermalService = IThermalService.Stub.asInterface(
+                        ServiceManager.getService(Context.THERMAL_SERVICE));
+            }
+            try {
+                if (mCallbackMap.containsKey(callback)) {
+                    throw new IllegalArgumentException("ThermalStatusCallback already registered");
+                }
+                IThermalStatusListener listener = new IThermalStatusListener.Stub() {
+                    @Override
+                    public void onStatusChange(int status) {
+                        executor.execute(() -> {
+                            callback.onStatusChange(status);
+                        });
+                    }
+                };
+                if (mThermalService.registerThermalStatusListener(listener)) {
+                    mCallbackMap.put(callback, listener);
+                } else {
+                    throw new RuntimeException("ThermalStatusCallback failed to register");
+                }
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * This function unregisters a callback for thermal status change.
+     *
+     * @param callback to be unregistered.
+     *
+     * see {@link #registerThermalStatusCallback}
+     */
+    public void unregisterThermalStatusCallback(ThermalStatusCallback callback) {
+        Preconditions.checkNotNull(callback, "callback cannnot be null");
+        synchronized (this) {
+            if (mThermalService == null) {
+                mThermalService = IThermalService.Stub.asInterface(
+                        ServiceManager.getService(Context.THERMAL_SERVICE));
+            }
+            try {
+                IThermalStatusListener listener = mCallbackMap.get(callback);
+                if (listener == null) {
+                    throw new IllegalArgumentException("ThermalStatusCallback not registered");
+                }
+                if (mThermalService.unregisterThermalStatusListener(listener)) {
+                    mCallbackMap.remove(callback);
+                } else {
+                    throw new RuntimeException("ThermalStatusCallback failed to unregister");
+                }
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * If true, the doze component is not started until after the screen has been
      * turned off and the screen off animation has been performed.
      * @hide
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index bf85fbd..5499181 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -28,7 +28,7 @@
  *
  * @hide
  */
-public class Temperature implements Parcelable {
+public final class Temperature implements Parcelable {
     /** Temperature value */
     private float mValue;
     /** A temperature type from ThermalHAL */
@@ -44,7 +44,7 @@
             THROTTLING_MODERATE,
             THROTTLING_SEVERE,
             THROTTLING_CRITICAL,
-            THROTTLING_WARNING,
+            THROTTLING_EMERGENCY,
             THROTTLING_SHUTDOWN,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -56,7 +56,7 @@
     public static final int THROTTLING_MODERATE = ThrottlingSeverity.MODERATE;
     public static final int THROTTLING_SEVERE = ThrottlingSeverity.SEVERE;
     public static final int THROTTLING_CRITICAL = ThrottlingSeverity.CRITICAL;
-    public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
+    public static final int THROTTLING_EMERGENCY = ThrottlingSeverity.EMERGENCY;
     public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
 
     @IntDef(prefix = { "TYPE_" }, value = {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8e11d85..d315383 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -56,6 +56,7 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.sysprop.VoldProperties;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -1495,7 +1496,7 @@
      * framework, so no service needs to check for changes during their lifespan
      */
     public static boolean isBlockEncrypting() {
-        final String state = SystemProperties.get("vold.encrypt_progress", "");
+        final String state = VoldProperties.encrypt_progress().orElse("");
         return !"".equalsIgnoreCase(state);
     }
 
@@ -1511,7 +1512,7 @@
      * framework, so no service needs to check for changes during their lifespan
      */
     public static boolean inCryptKeeperBounce() {
-        final String status = SystemProperties.get("vold.decrypt");
+        final String status = VoldProperties.decrypt().orElse("");
         return "trigger_restart_min_framework".equals(status);
     }
 
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 18aea03..a41a644 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,6 +16,9 @@
 
 package android.permissionpresenterservice;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -27,12 +30,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteCallback;
 
-import com.android.internal.os.SomeArgs;
-
 import java.util.List;
 
 /**
@@ -63,7 +62,7 @@
     @Override
     public final void attachBaseContext(Context base) {
         super.attachBaseContext(base);
-        mHandler = new MyHandler(base.getMainLooper());
+        mHandler = new Handler(base.getMainLooper());
     }
 
     /**
@@ -71,7 +70,8 @@
      *
      * @param packageName The package for which to query.
      */
-    public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
+    public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+            @NonNull String packageName);
 
     /**
      * Revokes the permission {@code permissionName} for app {@code packageName}
@@ -87,61 +87,35 @@
         return new IRuntimePermissionPresenter.Stub() {
             @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = packageName;
-                args.arg2 = callback;
-                mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS,
-                        args).sendToTarget();
+                checkNotNull(packageName, "packageName");
+                checkNotNull(callback, "callback");
+
+                mHandler.sendMessage(
+                        obtainMessage(RuntimePermissionPresenterService::getAppPermissions,
+                                RuntimePermissionPresenterService.this, packageName, callback));
             }
 
             @Override
             public void revokeRuntimePermission(String packageName, String permissionName) {
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = packageName;
-                args.arg2 = permissionName;
-                mHandler.obtainMessage(MyHandler.MSG_REVOKE_APP_PERMISSION,
-                        args).sendToTarget();
+                checkNotNull(packageName, "packageName");
+                checkNotNull(permissionName, "permissionName");
+
+                mHandler.sendMessage(
+                        obtainMessage(RuntimePermissionPresenterService::onRevokeRuntimePermission,
+                                RuntimePermissionPresenterService.this, packageName,
+                                permissionName));
             }
         };
     }
 
-    private final class MyHandler extends Handler {
-        public static final int MSG_GET_APP_PERMISSIONS = 1;
-        public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
-        public static final int MSG_REVOKE_APP_PERMISSION = 3;
-
-        public MyHandler(Looper looper) {
-            super(looper, null, false);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_GET_APP_PERMISSIONS: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    String packageName = (String) args.arg1;
-                    RemoteCallback callback = (RemoteCallback) args.arg2;
-                    args.recycle();
-                    List<RuntimePermissionPresentationInfo> permissions =
-                            onGetAppPermissions(packageName);
-                    if (permissions != null && !permissions.isEmpty()) {
-                        Bundle result = new Bundle();
-                        result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT,
-                                permissions);
-                        callback.sendResult(result);
-                    } else {
-                        callback.sendResult(null);
-                    }
-                } break;
-                case MSG_REVOKE_APP_PERMISSION: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    String packageName = (String) args.arg1;
-                    String permissionName = (String) args.arg2;
-                    args.recycle();
-
-                    onRevokeRuntimePermission(packageName, permissionName);
-                } break;
-            }
+    private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+        List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+        if (permissions != null && !permissions.isEmpty()) {
+            Bundle result = new Bundle();
+            result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+            callback.sendResult(result);
+        } else {
+            callback.sendResult(null);
         }
     }
 }
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index e032c18..37c84bd 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -152,6 +152,17 @@
             "android:query-arg-last-modified-after";
 
     /**
+     * Key for {@link DocumentsProvider} to decide whether the files that
+     * have been added to MediaStore should be excluded. If the value is
+     * true, exclude them. Otherwise, include them.
+     *
+     * @see DocumentsProvider#querySearchDocuments(String, String[],
+     *      Bundle)
+     * {@hide}
+     */
+    public static final String QUERY_ARG_EXCLUDE_MEDIA = "android:query-arg-exclude-media";
+
+    /**
      * Sets the desired initial location visible to user when file chooser is shown.
      *
      * <p>Applicable to {@link Intent} with actions:
@@ -1017,6 +1028,7 @@
 
     /**
      * Get the handled query arguments from the query bundle. The handled arguments are
+     * {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA},
      * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
      * {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
      * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and
@@ -1032,6 +1044,11 @@
         }
 
         final ArrayList<String> args = new ArrayList<>();
+
+        if (queryArgs.keySet().contains(QUERY_ARG_EXCLUDE_MEDIA)) {
+            args.add(QUERY_ARG_EXCLUDE_MEDIA);
+        }
+
         if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
             args.add(QUERY_ARG_DISPLAY_NAME);
         }
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 58f8213..6ab72c7 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -677,6 +677,7 @@
      *            cursor. If {@code null} all supported columns should be
      *            included.
      * @param queryArgs the query arguments.
+     *            {@link DocumentsContract#QUERY_ARG_EXCLUDE_MEDIA},
      *            {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
      *            {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
      *            {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER},
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 9e26a36..0299e41 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -37,6 +37,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Point;
+import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -1166,13 +1167,25 @@
             /**
              * The latitude where the image was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the image was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LONGITUDE = "longitude";
 
             /**
@@ -2410,13 +2423,25 @@
             /**
              * The latitude where the video was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the video was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LONGITUDE = "longitude";
 
             /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c297ef4..c3217a2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4252,6 +4252,7 @@
             PUBLIC_SETTINGS.add(BLUETOOTH_DISCOVERABILITY_TIMEOUT);
             PUBLIC_SETTINGS.add(NEXT_ALARM_FORMATTED);
             PUBLIC_SETTINGS.add(FONT_SCALE);
+            PUBLIC_SETTINGS.add(SYSTEM_LOCALES);
             PUBLIC_SETTINGS.add(DIM_SCREEN);
             PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
@@ -6279,6 +6280,7 @@
          *
          * @hide
          */
+        @SystemApi
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
                 "accessibility_display_magnification_navbar_enabled";
 
@@ -12837,6 +12839,23 @@
                 "max_sound_trigger_detection_service_ops_per_day";
 
         /**
+         * Property used by {@code com.android.server.SystemServer} on start to decide whether
+         * the Smart Suggestions service should be created or not
+         *
+         * <p>By default it should *NOT* be set (in which case the decision is based on whether
+         * the OEM provides an implementation for the service), but it can be overridden to:
+         *
+         * <ul>
+         *   <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
+         *   <li>Enable the CTS tests to be run on AOSP builds
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED =
+                "smart_suggestions_service_explicitly_enabled";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
@@ -13509,6 +13528,13 @@
          */
         public static final String WARNING_TEMPERATURE = "warning_temperature";
 
+
+        /**
+         * USB Temperature at which the high temperature alarm notification should be shown.
+         * @hide
+         */
+        public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature";
+
         /**
          * Whether the diskstats logging task is enabled/disabled.
          * @hide
diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java
index af2da79..ddf37f7 100644
--- a/core/java/android/service/intelligence/FillCallback.java
+++ b/core/java/android/service/intelligence/FillCallback.java
@@ -15,8 +15,10 @@
  */
 package android.service.intelligence;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
 
 /**
  * Callback used to indicate at {@link FillRequest} has been fulfilled.
@@ -25,8 +27,11 @@
  */
 @SystemApi
 public final class FillCallback {
+    private final AutofillProxy mProxy;
 
-    FillCallback() {}
+    FillCallback(@NonNull AutofillProxy proxy) {
+        mProxy = proxy;
+    }
 
     /**
      * Sets the response associated with the request.
@@ -35,6 +40,7 @@
      * could not provide autofill for the request.
      */
     public void onSuccess(@Nullable FillResponse response) {
+        mProxy.report(AutofillProxy.REPORT_EVENT_ON_SUCCESS);
         final FillWindow fillWindow = response.getFillWindow();
         if (fillWindow != null) {
             fillWindow.show();
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
index f68db9d..53e99a5 100644
--- a/core/java/android/service/intelligence/FillRequest.java
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 
 /**
  * Represents a request to augment-fill an activity.
@@ -52,6 +53,14 @@
     }
 
     /**
+     * Gets the current value of the field that triggered the request.
+     */
+    @NonNull
+    public AutofillValue getFocusedAutofillValue() {
+        return mProxy.focusedValue;
+    }
+
+    /**
      * 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.
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
index 309f6a1..39d7e08 100644
--- a/core/java/android/service/intelligence/FillWindow.java
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -23,6 +23,7 @@
 import android.app.Dialog;
 import android.graphics.Rect;
 import android.service.intelligence.PresentationParams.Area;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
@@ -33,6 +34,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import dalvik.system.CloseGuard;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -73,6 +76,7 @@
     @interface Flags{}
 
     private final Object mLock = new Object();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
     @GuardedBy("mLock")
     private Dialog mDialog;
@@ -80,6 +84,8 @@
     @GuardedBy("mLock")
     private boolean mDestroyed;
 
+    private AutofillProxy mProxy;
+
     /**
      * Updates the content of the window.
      *
@@ -123,6 +129,8 @@
         synchronized (mLock) {
             checkNotDestroyedLocked();
 
+            mProxy = area.proxy;
+
             // 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
@@ -133,6 +141,7 @@
             // etc.
 
             mDialog = new Dialog(rootView.getContext());
+            mCloseGuard.open("destroy");
             final Window window = mDialog.getWindow();
             window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
 
@@ -156,7 +165,7 @@
                 Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
             }
 
-            area.proxy.setFillWindow(this);
+            mProxy.setFillWindow(this);
             return true;
         }
     }
@@ -173,6 +182,9 @@
             }
 
             mDialog.show();
+            if (mProxy != null) {
+                mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
+            }
         }
     }
 
@@ -182,15 +194,29 @@
      * <p>Once destroyed, this window cannot be used anymore
      */
     public void destroy() {
-        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed);
+        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
 
         synchronized (this) {
-            if (mDestroyed) return;
+            if (mDestroyed || mDialog == null) return;
 
-            if (mDialog != null) {
-                mDialog.dismiss();
-                mDialog = null;
+            mDialog.dismiss();
+            mDialog = null;
+            if (mProxy != null) {
+                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
             }
+            mCloseGuard.close();
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            destroy();
+        } finally {
+            super.finalize();
         }
     }
 
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index d6b3107..2b924fb 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -23,6 +23,7 @@
 import android.service.intelligence.SnapshotData;
 
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 import android.view.intelligence.ContentCaptureEvent;
 
 import java.util.List;
@@ -45,7 +46,8 @@
                             in SnapshotData snapshotData);
 
     void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient,
-                           int autofilSessionId, in AutofillId focusedId);
+                           int autofilSessionId, in AutofillId focusedId,
+                           in AutofillValue focusedValue, long requestTime);
 
     void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId);
 }
diff --git a/core/java/android/service/intelligence/SmartSuggestionsService.java b/core/java/android/service/intelligence/SmartSuggestionsService.java
index 0e29e70..b684b02 100644
--- a/core/java/android/service/intelligence/SmartSuggestionsService.java
+++ b/core/java/android/service/intelligence/SmartSuggestionsService.java
@@ -18,6 +18,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -30,10 +31,13 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
+import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAugmentedAutofillManagerClient;
@@ -43,6 +47,8 @@
 
 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.List;
 import java.util.Set;
@@ -110,9 +116,11 @@
 
         @Override
         public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
-                int autofilSessionId, AutofillId focusedId) {
+                int autofilSessionId, AutofillId focusedId, AutofillValue focusedValue,
+                long requestTime) {
             mHandler.sendMessage(obtainMessage(SmartSuggestionsService::handleOnAutofillRequest,
-                    SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId));
+                    SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId,
+                    focusedValue, requestTime));
         }
 
         @Override
@@ -229,13 +237,15 @@
             @NonNull ContentCaptureEventsRequest request);
 
     private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
-            @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
+            @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId,
+            @Nullable AutofillValue focusedValue, long requestTime) {
         if (mAutofillProxies == null) {
             mAutofillProxies = new ArrayMap<>();
         }
         AutofillProxy proxy = mAutofillProxies.get(sessionId);
         if (proxy == null) {
-            proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId);
+            proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId, focusedValue,
+                    requestTime);
             mAutofillProxies.put(sessionId,  proxy);
         } else {
             // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
@@ -244,7 +254,7 @@
         // TODO(b/111330312): set cancellation signal
         final CancellationSignal cancellationSignal = null;
         onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
-                new FillController(proxy), new FillCallback());
+                new FillController(proxy), new FillCallback(proxy));
     }
 
     /**
@@ -332,11 +342,32 @@
 
     /** @hide */
     static final class AutofillProxy {
+
+        static final int REPORT_EVENT_ON_SUCCESS = 1;
+        static final int REPORT_EVENT_UI_SHOWN = 2;
+        static final int REPORT_EVENT_UI_DESTROYED = 3;
+
+        @IntDef(prefix = { "REPORT_EVENT_" }, value = {
+                REPORT_EVENT_ON_SUCCESS,
+                REPORT_EVENT_UI_SHOWN,
+                REPORT_EVENT_UI_DESTROYED
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface ReportEvent{}
+
+
         private final Object mLock = new Object();
         private final IAugmentedAutofillManagerClient mClient;
         private final int mAutofillSessionId;
         public final InteractionSessionId sessionId;
         public final AutofillId focusedId;
+        public final AutofillValue focusedValue;
+
+        // Objects used to log metrics
+        private final long mRequestTime;
+        private long mOnSuccessTime;
+        private long mUiFirstShownTime;
+        private long mUiFirstDestroyedTime;
 
         @GuardedBy("mLock")
         private SystemPopupPresentationParams mSmartSuggestion;
@@ -345,11 +376,14 @@
         private FillWindow mFillWindow;
 
         private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
-                int autofillSessionId, @NonNull AutofillId focusedId) {
+                int autofillSessionId, @NonNull AutofillId focusedId,
+                @Nullable AutofillValue focusedValue, long requestTime) {
             this.sessionId = sessionId;
             mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
             mAutofillSessionId = autofillSessionId;
             this.focusedId = focusedId;
+            this.focusedValue = focusedValue;
+            this.mRequestTime = requestTime;
             // TODO(b/111330312): linkToDeath
         }
 
@@ -400,9 +434,50 @@
             }
         }
 
+        // Used for metrics.
+        public void report(@ReportEvent int event) {
+            switch (event) {
+                case REPORT_EVENT_ON_SUCCESS:
+                    if (mOnSuccessTime == 0) {
+                        mOnSuccessTime = SystemClock.elapsedRealtime();
+                        if (DEBUG) {
+                            Slog.d(TAG, "Service responsed in "
+                                    + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime));
+                        }
+                    }
+                    break;
+                case REPORT_EVENT_UI_SHOWN:
+                    if (mUiFirstShownTime == 0) {
+                        mUiFirstShownTime = SystemClock.elapsedRealtime();
+                        if (DEBUG) {
+                            Slog.d(TAG, "UI shown in "
+                                    + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime));
+                        }
+                    }
+                    break;
+                case REPORT_EVENT_UI_DESTROYED:
+                    if (mUiFirstDestroyedTime == 0) {
+                        mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
+                        if (DEBUG) {
+                            Slog.d(TAG, "UI destroyed in "
+                                    + TimeUtils.formatDuration(
+                                            mUiFirstDestroyedTime - mRequestTime));
+                        }
+                    }
+                    break;
+                default:
+                    Slog.w(TAG, "invalid event reported: " + event);
+            }
+            // TODO(b/111330312): log metrics as well
+        }
+
+
         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);
+            if (focusedValue != null) {
+                pw.print(prefix); pw.print("focusedValue: "); pw.println(focusedValue);
+            }
             pw.print(prefix); pw.print("client: "); pw.println(mClient);
             final String prefix2 = prefix + "  ";
             if (mFillWindow != null) {
@@ -413,6 +488,23 @@
                 pw.print(prefix); pw.println("smartSuggestion:");
                 mSmartSuggestion.dump(prefix2, pw);
             }
+            if (mOnSuccessTime > 0) {
+                final long responseTime = mOnSuccessTime - mRequestTime;
+                pw.print(prefix); pw.print("response time: ");
+                TimeUtils.formatDuration(responseTime, pw); pw.println();
+            }
+
+            if (mUiFirstShownTime > 0) {
+                final long uiRenderingTime = mUiFirstShownTime - mRequestTime;
+                pw.print(prefix); pw.print("UI rendering time: ");
+                TimeUtils.formatDuration(uiRenderingTime, pw); pw.println();
+            }
+
+            if (mUiFirstDestroyedTime > 0) {
+                final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime;
+                pw.print(prefix); pw.print("UI life time: ");
+                TimeUtils.formatDuration(uiTotalTime, pw); pw.println();
+            }
         }
 
         private void destroy() {
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 3ee5f1f..33b3ff4f 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 
@@ -225,7 +226,7 @@
     private int mMinimumFlingVelocity;
     private int mMaximumFlingVelocity;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
     private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
     private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
@@ -592,8 +593,8 @@
 
                 if (mIsLongpressEnabled) {
                     mHandler.removeMessages(LONG_PRESS);
-                    mHandler.sendEmptyMessageAtTime(LONG_PRESS,
-                            mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
+                    mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+                            + ViewConfiguration.getLongPressTimeout());
                 }
                 mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
                         mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8b39cc7..9cced4e 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1803,6 +1803,28 @@
     }
 
     /**
+     * Returns whether this key will be sent to the
+     * {@link android.media.session.MediaSession.Callback} if not handled.
+     */
+    public static final boolean isMediaSessionKey(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                return true;
+        }
+        return false;
+    }
+
+    /**
      * Returns true if the specified keycode is a gamepad button.
      * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
      */
@@ -1861,31 +1883,6 @@
         }
     }
 
-    /**
-     * Returns whether this key can be handled by
-     * {@link android.media.session.MediaSession.Callback}.
-     *
-     * @hide
-     */
-    public static final boolean isMediaSessionKey(int keyCode) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                return true;
-        }
-        return false;
-    }
-
-
     /** Is this a system key? System keys can not be used for menu shortcuts.
      * @hide
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4b9cbff..bd2aa64 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -112,6 +112,9 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inspector.InspectableProperty;
+import android.view.inspector.InspectableProperty.EnumMap;
+import android.view.inspector.InspectableProperty.FlagMap;
 import android.view.intelligence.ContentCaptureManager;
 import android.widget.Checkable;
 import android.widget.FrameLayout;
@@ -4895,6 +4898,15 @@
      */
     public static final int LAYER_TYPE_HARDWARE = 2;
 
+    /** @hide */
+    @IntDef(prefix = { "LAYER_TYPE_" }, value = {
+            LAYER_TYPE_NONE,
+            LAYER_TYPE_SOFTWARE,
+            LAYER_TYPE_HARDWARE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LayerType {}
+
     @ViewDebug.ExportedProperty(category = "drawing", mapping = {
             @ViewDebug.IntToString(from = LAYER_TYPE_NONE, to = "NONE"),
             @ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to = "SOFTWARE"),
@@ -6516,6 +6528,15 @@
      *
      * @return a bitmask representing the enabled scroll indicators
      */
+    @InspectableProperty(flagMapping = {
+            @FlagMap(target = SCROLL_INDICATORS_NONE, mask = 0xffff_ffff, name = "none"),
+            @FlagMap(target = SCROLL_INDICATOR_TOP, name = "top"),
+            @FlagMap(target = SCROLL_INDICATOR_BOTTOM, name = "bottom"),
+            @FlagMap(target = SCROLL_INDICATOR_LEFT, name = "left"),
+            @FlagMap(target = SCROLL_INDICATOR_RIGHT, name = "right"),
+            @FlagMap(target = SCROLL_INDICATOR_START, name = "start"),
+            @FlagMap(target = SCROLL_INDICATOR_END, name = "end")
+    })
     @ScrollIndicators
     public int getScrollIndicators() {
         return (mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK)
@@ -7582,7 +7603,9 @@
      *
      * {@see #setAccessibilityPaneTitle}.
      */
-    @Nullable public CharSequence getAccessibilityPaneTitle() {
+    @InspectableProperty
+    @Nullable
+    public CharSequence getAccessibilityPaneTitle() {
         return mAccessibilityPaneTitle;
     }
 
@@ -8580,6 +8603,7 @@
      * @attr ref android.R.styleable#View_autofillHints
      */
     @ViewDebug.ExportedProperty()
+    @InspectableProperty
     @Nullable public String[] getAutofillHints() {
         return mAutofillHints;
     }
@@ -8624,6 +8648,15 @@
                 to = "yesExcludeDescendants"),
             @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
                 to = "noExcludeDescendants")})
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = IMPORTANT_FOR_AUTOFILL_AUTO, name = "auto"),
+            @EnumMap(value = IMPORTANT_FOR_AUTOFILL_YES, name = "yes"),
+            @EnumMap(value = IMPORTANT_FOR_AUTOFILL_NO, name = "no"),
+            @EnumMap(value = IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
+                    name = "yesExcludeDescendants"),
+            @EnumMap(value = IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
+                    name = "noExcludeDescendants"),
+    })
     public @AutofillImportance int getImportantForAutofill() {
         return (mPrivateFlags3
                 & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
@@ -8808,6 +8841,15 @@
                 to = "yesExcludeDescendants"),
             @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
                 to = "noExcludeDescendants")})
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"),
+            @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"),
+            @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"),
+            @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+                    name = "yesExcludeDescendants"),
+            @EnumMap(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
+                    name = "noExcludeDescendants"),
+    })
     public @ContentCaptureImportance int getImportantForContentCapture() {
         // NOTE: the important for content capture values were the first flags added and are set in
         // the rightmost position, so we don't need to shift them
@@ -9619,6 +9661,7 @@
      * @attr ref android.R.styleable#View_contentDescription
      */
     @ViewDebug.ExportedProperty(category = "accessibility")
+    @InspectableProperty
     public CharSequence getContentDescription() {
         return mContentDescription;
     }
@@ -9699,6 +9742,7 @@
      *
      * @see #setAccessibilityTraversalBefore(int)
      */
+    @InspectableProperty
     public int getAccessibilityTraversalBefore() {
         return mAccessibilityTraversalBeforeId;
     }
@@ -9743,6 +9787,7 @@
      *
      * @see #setAccessibilityTraversalAfter(int)
      */
+    @InspectableProperty
     public int getAccessibilityTraversalAfter() {
         return mAccessibilityTraversalAfterId;
     }
@@ -9754,6 +9799,7 @@
      * @return The labeled view id.
      */
     @ViewDebug.ExportedProperty(category = "accessibility")
+    @InspectableProperty
     public int getLabelFor() {
         return mLabelForId;
     }
@@ -9817,6 +9863,7 @@
      * @return True if this view has focus, false otherwise.
      */
     @ViewDebug.ExportedProperty(category = "focus")
+    @InspectableProperty(hasAttributeId = false)
     public boolean isFocused() {
         return (mPrivateFlags & PFLAG_FOCUSED) != 0;
     }
@@ -9841,6 +9888,7 @@
      *
      * @attr ref android.R.styleable#View_isScrollContainer
      */
+    @InspectableProperty(name = "isScrollContainer")
     public boolean isScrollContainer() {
         return (mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0;
     }
@@ -9896,6 +9944,11 @@
      */
     @Deprecated
     @DrawingCacheQuality
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = DRAWING_CACHE_QUALITY_LOW, name = "low"),
+            @EnumMap(value = DRAWING_CACHE_QUALITY_HIGH, name = "high"),
+            @EnumMap(value = DRAWING_CACHE_QUALITY_AUTO, name = "auto")
+    })
     public int getDrawingCacheQuality() {
         return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
     }
@@ -9941,6 +9994,7 @@
      *
      * @attr ref android.R.styleable#View_keepScreenOn
      */
+    @InspectableProperty
     public boolean getKeepScreenOn() {
         return (mViewFlags & KEEP_SCREEN_ON) != 0;
     }
@@ -9965,6 +10019,7 @@
      *
      * @attr ref android.R.styleable#View_nextFocusLeft
      */
+    @InspectableProperty(name = "nextFocusLeft")
     public int getNextFocusLeftId() {
         return mNextFocusLeftId;
     }
@@ -9986,6 +10041,7 @@
      *
      * @attr ref android.R.styleable#View_nextFocusRight
      */
+    @InspectableProperty(name = "nextFocusRight")
     public int getNextFocusRightId() {
         return mNextFocusRightId;
     }
@@ -10007,6 +10063,7 @@
      *
      * @attr ref android.R.styleable#View_nextFocusUp
      */
+    @InspectableProperty(name = "nextFocusUp")
     public int getNextFocusUpId() {
         return mNextFocusUpId;
     }
@@ -10028,6 +10085,7 @@
      *
      * @attr ref android.R.styleable#View_nextFocusDown
      */
+    @InspectableProperty(name = "nextFocusDown")
     public int getNextFocusDownId() {
         return mNextFocusDownId;
     }
@@ -10049,6 +10107,7 @@
      *
      * @attr ref android.R.styleable#View_nextFocusForward
      */
+    @InspectableProperty(name = "nextFocusForward")
     public int getNextFocusForwardId() {
         return mNextFocusForwardId;
     }
@@ -10071,6 +10130,7 @@
      *
      * @attr ref android.R.styleable#View_nextClusterForward
      */
+    @InspectableProperty(name = "nextClusterForward")
     public int getNextClusterForwardId() {
         return mNextClusterForwardId;
     }
@@ -10437,6 +10497,7 @@
      * @see #setSystemUiVisibility(int)
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public boolean getFitsSystemWindows() {
         return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
     }
@@ -10498,6 +10559,11 @@
         @ViewDebug.IntToString(from = INVISIBLE, to = "INVISIBLE"),
         @ViewDebug.IntToString(from = GONE,      to = "GONE")
     })
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = VISIBLE, name = "visible"),
+            @EnumMap(value = INVISIBLE, name = "invisible"),
+            @EnumMap(value = GONE, name = "gone")
+    })
     @Visibility
     public int getVisibility() {
         return mViewFlags & VISIBILITY_MASK;
@@ -10521,6 +10587,7 @@
      * @return True if this view is enabled, false otherwise.
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public boolean isEnabled() {
         return (mViewFlags & ENABLED_MASK) == ENABLED;
     }
@@ -10690,6 +10757,7 @@
      * @attr ref android.R.styleable#View_soundEffectsEnabled
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public boolean isSoundEffectsEnabled() {
         return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
     }
@@ -10719,6 +10787,7 @@
      * @attr ref android.R.styleable#View_hapticFeedbackEnabled
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public boolean isHapticFeedbackEnabled() {
         return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
     }
@@ -10741,6 +10810,12 @@
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"),
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE,  to = "LOCALE")
     })
+    @InspectableProperty(hasAttributeId = false, enumMapping = {
+            @EnumMap(value = LAYOUT_DIRECTION_LTR, name = "ltr"),
+            @EnumMap(value = LAYOUT_DIRECTION_RTL, name = "rtl"),
+            @EnumMap(value = LAYOUT_DIRECTION_INHERIT, name = "inherit"),
+            @EnumMap(value = LAYOUT_DIRECTION_LOCALE, name = "locale")
+    })
     @LayoutDir
     public int getRawLayoutDirection() {
         return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT;
@@ -10794,6 +10869,10 @@
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"),
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
     })
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = LAYOUT_DIRECTION_LTR, name = "ltr"),
+            @EnumMap(value = LAYOUT_DIRECTION_RTL, name = "rtl")
+    })
     @ResolvedLayoutDir
     public int getLayoutDirection() {
         final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
@@ -10980,6 +11059,7 @@
      * @attr ref android.R.styleable#View_clickable
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public boolean isClickable() {
         return (mViewFlags & CLICKABLE) == CLICKABLE;
     }
@@ -11007,6 +11087,7 @@
      * @see #setLongClickable(boolean)
      * @attr ref android.R.styleable#View_longClickable
      */
+    @InspectableProperty
     public boolean isLongClickable() {
         return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
     }
@@ -11032,6 +11113,7 @@
      * @see #setContextClickable(boolean)
      * @attr ref android.R.styleable#View_contextClickable
      */
+    @InspectableProperty
     public boolean isContextClickable() {
         return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
     }
@@ -11111,6 +11193,7 @@
      * @return true if the view is currently pressed, false otherwise
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty(hasAttributeId = false)
     public boolean isPressed() {
         return (mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED;
     }
@@ -11126,6 +11209,7 @@
      * @see #setAssistBlocked(boolean)
      * @attr ref android.R.styleable#View_assistBlocked
      */
+    @InspectableProperty
     public boolean isAssistBlocked() {
         return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0;
     }
@@ -11163,6 +11247,7 @@
      * @see #setSaveEnabled(boolean)
      * @attr ref android.R.styleable#View_saveEnabled
      */
+    @InspectableProperty
     public boolean isSaveEnabled() {
         return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED;
     }
@@ -11198,6 +11283,7 @@
      * @attr ref android.R.styleable#View_filterTouchesWhenObscured
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public boolean getFilterTouchesWhenObscured() {
         return (mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0;
     }
@@ -11270,6 +11356,11 @@
             @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"),
             @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO")
             }, category = "focus")
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = NOT_FOCUSABLE, name = "false"),
+            @EnumMap(value = FOCUSABLE, name = "true"),
+            @EnumMap(value = FOCUSABLE_AUTO, name = "auto")
+    })
     @Focusable
     public int getFocusable() {
         return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE;
@@ -11284,6 +11375,7 @@
      * @attr ref android.R.styleable#View_focusableInTouchMode
      */
     @ViewDebug.ExportedProperty(category = "focus")
+    @InspectableProperty
     public final boolean isFocusableInTouchMode() {
         return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE);
     }
@@ -11295,6 +11387,7 @@
      *
      * @return Whether the view should be treated as a focusable unit by screen reader.
      */
+    @InspectableProperty
     public boolean isScreenReaderFocusable() {
         return (mPrivateFlags3 & PFLAG3_SCREEN_READER_FOCUSABLE) != 0;
     }
@@ -11323,6 +11416,7 @@
      *
      * @attr ref android.R.styleable#View_accessibilityHeading
      */
+    @InspectableProperty
     public boolean isAccessibilityHeading() {
         return (mPrivateFlags3 & PFLAG3_ACCESSIBILITY_HEADING) != 0;
     }
@@ -11377,6 +11471,7 @@
      * @attr ref android.R.styleable#View_keyboardNavigationCluster
      */
     @ViewDebug.ExportedProperty(category = "focus")
+    @InspectableProperty
     public final boolean isKeyboardNavigationCluster() {
         return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
     }
@@ -11486,6 +11581,7 @@
      * @attr ref android.R.styleable#View_focusedByDefault
      */
     @ViewDebug.ExportedProperty(category = "focus")
+    @InspectableProperty
     public final boolean isFocusedByDefault() {
         return (mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0;
     }
@@ -11599,6 +11695,7 @@
      * @attr ref android.R.styleable#View_defaultFocusHighlightEnabled
      */
     @ViewDebug.ExportedProperty(category = "focus")
+    @InspectableProperty
     public final boolean getDefaultFocusHighlightEnabled() {
         return mDefaultFocusHighlightEnabled;
     }
@@ -11817,6 +11914,7 @@
      *
      * @return True if this View is accessibility focused.
      */
+    @InspectableProperty(hasAttributeId = false)
     public boolean isAccessibilityFocused() {
         return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0;
     }
@@ -12143,6 +12241,13 @@
             @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
                     to = "noHideDescendants")
         })
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_AUTO, name = "auto"),
+            @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_YES, name = "yes"),
+            @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_NO, name = "no"),
+            @EnumMap(value = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
+                    name = "noHideDescendants"),
+    })
     public int getImportantForAccessibility() {
         return (mPrivateFlags2 & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK)
                 >> PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
@@ -12195,6 +12300,11 @@
      *
      * @see #setAccessibilityLiveRegion(int)
      */
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = ACCESSIBILITY_LIVE_REGION_NONE, name = "none"),
+            @EnumMap(value = ACCESSIBILITY_LIVE_REGION_POLITE, name = "polite"),
+            @EnumMap(value = ACCESSIBILITY_LIVE_REGION_ASSERTIVE, name = "assertive")
+    })
     public int getAccessibilityLiveRegion() {
         return (mPrivateFlags2 & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK)
                 >> PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT;
@@ -15042,6 +15152,7 @@
      *
      * @return The left edge of the displayed part of your view, in pixels.
      */
+    @InspectableProperty
     public final int getScrollX() {
         return mScrollX;
     }
@@ -15053,6 +15164,7 @@
      *
      * @return The top edge of the displayed part of your view, in pixels.
      */
+    @InspectableProperty
     public final int getScrollY() {
         return mScrollY;
     }
@@ -15289,6 +15401,7 @@
      * @return The degrees of rotation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getRotation() {
         return mRenderNode.getRotation();
     }
@@ -15329,6 +15442,7 @@
      * @return The degrees of Y rotation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getRotationY() {
         return mRenderNode.getRotationY();
     }
@@ -15373,6 +15487,7 @@
      * @return The degrees of X rotation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getRotationX() {
         return mRenderNode.getRotationX();
     }
@@ -15418,6 +15533,7 @@
      * @return The scaling factor.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getScaleX() {
         return mRenderNode.getScaleX();
     }
@@ -15455,6 +15571,7 @@
      * @return The scaling factor.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getScaleY() {
         return mRenderNode.getScaleY();
     }
@@ -15494,6 +15611,7 @@
      * @attr ref android.R.styleable#View_transformPivotX
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty(name = "transformPivotX")
     public float getPivotX() {
         return mRenderNode.getPivotX();
     }
@@ -15536,6 +15654,7 @@
      * @attr ref android.R.styleable#View_transformPivotY
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty(name = "transformPivotY")
     public float getPivotY() {
         return mRenderNode.getPivotY();
     }
@@ -15594,6 +15713,7 @@
      * @return The opacity of the view.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getAlpha() {
         return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
     }
@@ -15816,6 +15936,7 @@
      * @return true if force dark is allowed (default), false if it is disabled
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public boolean isForceDarkAllowed() {
         return mRenderNode.isForceDarkAllowed();
     }
@@ -16187,6 +16308,7 @@
      * @return The base depth position of the view, in pixels.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getElevation() {
         return mRenderNode.getElevation();
     }
@@ -16215,6 +16337,7 @@
      * @return The horizontal position of this view relative to its left position, in pixels.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getTranslationX() {
         return mRenderNode.getTranslationX();
     }
@@ -16249,6 +16372,7 @@
      * in pixels.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getTranslationY() {
         return mRenderNode.getTranslationY();
     }
@@ -16280,6 +16404,7 @@
      * @return The depth of this view relative to its elevation.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @InspectableProperty
     public float getTranslationZ() {
         return mRenderNode.getTranslationZ();
     }
@@ -16324,6 +16449,7 @@
      * @return StateListAnimator or null if it does not exists
      * @see    #setStateListAnimator(android.animation.StateListAnimator)
      */
+    @InspectableProperty
     public StateListAnimator getStateListAnimator() {
         return mStateListAnimator;
     }
@@ -16437,6 +16563,7 @@
      *
      * @see #setOutlineProvider(ViewOutlineProvider)
      */
+    @InspectableProperty
     public ViewOutlineProvider getOutlineProvider() {
         return mOutlineProvider;
     }
@@ -16513,6 +16640,7 @@
      * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing
      * was set
      */
+    @InspectableProperty
     public @ColorInt int getOutlineSpotShadowColor() {
         return mRenderNode.getSpotShadowColor();
     }
@@ -16541,6 +16669,7 @@
      * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if
      * nothing was set
      */
+    @InspectableProperty
     public @ColorInt int getOutlineAmbientShadowColor() {
         return mRenderNode.getAmbientShadowColor();
     }
@@ -17720,6 +17849,36 @@
     }
 
     /**
+     * Get the fading edge flags, used for inspection.
+     *
+     * @return One of {@link #FADING_EDGE_NONE}, {@link #FADING_EDGE_VERTICAL},
+     *         or {@link #FADING_EDGE_HORIZONTAL}
+     * @hide
+     */
+    @InspectableProperty(name = "requiresFadingEdge", flagMapping = {
+            @FlagMap(target = FADING_EDGE_NONE, mask = FADING_EDGE_MASK, name = "none"),
+            @FlagMap(target = FADING_EDGE_VERTICAL, name = "vertical"),
+            @FlagMap(target = FADING_EDGE_HORIZONTAL, name = "horizontal")
+    })
+    int getFadingEdge() {
+        return mViewFlags & FADING_EDGE_MASK;
+    }
+
+    /**
+     * Get the fading edge length, used for inspection
+     *
+     * @return The fading edge length or 0
+     * @hide
+     */
+    @InspectableProperty
+    int getFadingEdgeLength() {
+        if (mScrollCache != null && (mViewFlags & FADING_EDGE_MASK) != FADING_EDGE_NONE) {
+            return mScrollCache.fadingEdgeLength;
+        }
+        return 0;
+    }
+
+    /**
      * Returns the strength, or intensity, of the top faded edge. The strength is
      * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
      * returns 0.0 or 1.0 but no value in between.
@@ -17883,6 +18042,7 @@
      *
      * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
      */
+    @InspectableProperty
     public int getScrollBarDefaultDelayBeforeFade() {
         return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
                 mScrollCache.scrollBarDefaultDelayBeforeFade;
@@ -17907,6 +18067,7 @@
      *
      * @attr ref android.R.styleable#View_scrollbarFadeDuration
      */
+    @InspectableProperty
     public int getScrollBarFadeDuration() {
         return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
                 mScrollCache.scrollBarFadeDuration;
@@ -17931,6 +18092,7 @@
      *
      * @attr ref android.R.styleable#View_scrollbarSize
      */
+    @InspectableProperty
     public int getScrollBarSize() {
         return mScrollCache == null ? ViewConfiguration.get(mContext).getScaledScrollBarSize() :
                 mScrollCache.scrollBarSize;
@@ -17990,6 +18152,12 @@
             @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_OVERLAY, to = "OUTSIDE_OVERLAY"),
             @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_INSET, to = "OUTSIDE_INSET")
     })
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = SCROLLBARS_INSIDE_OVERLAY, name = "insideOverlay"),
+            @EnumMap(value = SCROLLBARS_INSIDE_INSET, name = "insideInset"),
+            @EnumMap(value = SCROLLBARS_OUTSIDE_OVERLAY, name = "outsideOverlay"),
+            @EnumMap(value = SCROLLBARS_OUTSIDE_INSET, name = "outsideInset")
+    })
     @ScrollBarStyle
     public int getScrollBarStyle() {
         return mViewFlags & SCROLLBARS_STYLE_MASK;
@@ -19411,6 +19579,7 @@
      * @see #getDrawableState()
      * @see #setDuplicateParentStateEnabled(boolean)
      */
+    @InspectableProperty(name = "duplicateParentState")
     public boolean isDuplicateParentStateEnabled() {
         return (mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE;
     }
@@ -19452,7 +19621,7 @@
      *
      * @attr ref android.R.styleable#View_layerType
      */
-    public void setLayerType(int layerType, @Nullable Paint paint) {
+    public void setLayerType(@LayerType int layerType, @Nullable Paint paint) {
         if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
             throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
                     + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
@@ -19536,6 +19705,12 @@
      * @see #LAYER_TYPE_SOFTWARE
      * @see #LAYER_TYPE_HARDWARE
      */
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = LAYER_TYPE_NONE, name = "none"),
+            @EnumMap(value = LAYER_TYPE_SOFTWARE, name = "software"),
+            @EnumMap(value = LAYER_TYPE_HARDWARE, name = "hardware")
+    })
+    @LayerType
     public int getLayerType() {
         return mLayerType;
     }
@@ -22312,6 +22487,7 @@
      *
      * @attr ref android.R.styleable#View_background
      */
+    @InspectableProperty
     public Drawable getBackground() {
         return mBackground;
     }
@@ -22347,6 +22523,7 @@
      * @attr ref android.R.styleable#View_backgroundTint
      * @see #setBackgroundTintList(ColorStateList)
      */
+    @InspectableProperty(name = "backgroundTint")
     @Nullable
     public ColorStateList getBackgroundTintList() {
         return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
@@ -22383,6 +22560,7 @@
      * @see #setBackgroundTintMode(PorterDuff.Mode)
      */
     @Nullable
+    @InspectableProperty
     public PorterDuff.Mode getBackgroundTintMode() {
         return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
     }
@@ -22418,6 +22596,7 @@
      *
      * @see #onDrawForeground(Canvas)
      */
+    @InspectableProperty
     public Drawable getForeground() {
         return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
     }
@@ -22496,6 +22675,7 @@
      *
      * @attr ref android.R.styleable#View_foregroundGravity
      */
+    @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
     public int getForegroundGravity() {
         return mForegroundInfo != null ? mForegroundInfo.mGravity
                 : Gravity.START | Gravity.TOP;
@@ -22563,6 +22743,7 @@
      * @attr ref android.R.styleable#View_foregroundTint
      * @see #setForegroundTintList(ColorStateList)
      */
+    @InspectableProperty(name = "foregroundTint")
     @Nullable
     public ColorStateList getForegroundTintList() {
         return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
@@ -22602,6 +22783,7 @@
      * @attr ref android.R.styleable#View_foregroundTintMode
      * @see #setForegroundTintMode(PorterDuff.Mode)
      */
+    @InspectableProperty
     @Nullable
     public PorterDuff.Mode getForegroundTintMode() {
         return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
@@ -22846,6 +23028,7 @@
      *
      * @return the top padding in pixels
      */
+    @InspectableProperty
     public int getPaddingTop() {
         return mPaddingTop;
     }
@@ -22857,6 +23040,7 @@
      *
      * @return the bottom padding in pixels
      */
+    @InspectableProperty
     public int getPaddingBottom() {
         return mPaddingBottom;
     }
@@ -22868,6 +23052,7 @@
      *
      * @return the left padding in pixels
      */
+    @InspectableProperty
     public int getPaddingLeft() {
         if (!isPaddingResolved()) {
             resolvePadding();
@@ -22897,6 +23082,7 @@
      *
      * @return the right padding in pixels
      */
+    @InspectableProperty
     public int getPaddingRight() {
         if (!isPaddingResolved()) {
             resolvePadding();
@@ -23019,6 +23205,7 @@
      * @return true if the view is selected, false otherwise
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty(hasAttributeId = false)
     public boolean isSelected() {
         return (mPrivateFlags & PFLAG_SELECTED) != 0;
     }
@@ -23062,6 +23249,7 @@
      * @return true if the view is activated, false otherwise
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty(hasAttributeId = false)
     public boolean isActivated() {
         return (mPrivateFlags & PFLAG_ACTIVATED) != 0;
     }
@@ -23560,6 +23748,7 @@
      */
     @IdRes
     @ViewDebug.CapturedViewProperty
+    @InspectableProperty
     public int getId() {
         return mID;
     }
@@ -23584,6 +23773,7 @@
      * @see #getTag(int)
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public Object getTag() {
         return mTag;
     }
@@ -23780,6 +23970,7 @@
      *         if baseline alignment is not supported
      */
     @ViewDebug.ExportedProperty(category = "layout")
+    @InspectableProperty
     public int getBaseline() {
         return -1;
     }
@@ -24150,6 +24341,7 @@
      *
      * @attr ref android.R.styleable#View_minHeight
      */
+    @InspectableProperty(name = "minHeight")
     public int getMinimumHeight() {
         return mMinHeight;
     }
@@ -24180,6 +24372,7 @@
      *
      * @attr ref android.R.styleable#View_minWidth
      */
+    @InspectableProperty(name = "minWidth")
     public int getMinimumWidth() {
         return mMinWidth;
     }
@@ -25199,6 +25392,11 @@
      *
      * @return This view's over-scroll mode.
      */
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = OVER_SCROLL_ALWAYS, name = "always"),
+            @EnumMap(value = OVER_SCROLL_IF_CONTENT_SCROLLS, name = "ifContentScrolls"),
+            @EnumMap(value = OVER_SCROLL_NEVER, name = "never")
+    })
     public int getOverScrollMode() {
         return mOverScrollMode;
     }
@@ -25257,6 +25455,7 @@
      *
      * @see #setNestedScrollingEnabled(boolean)
      */
+    @InspectableProperty
     public boolean isNestedScrollingEnabled() {
         return (mPrivateFlags3 & PFLAG3_NESTED_SCROLLING_ENABLED) ==
                 PFLAG3_NESTED_SCROLLING_ENABLED;
@@ -25586,6 +25785,16 @@
             @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
     })
+    @InspectableProperty(hasAttributeId = false, enumMapping = {
+            @EnumMap(value = TEXT_DIRECTION_INHERIT, name = "inherit"),
+            @EnumMap(value = TEXT_DIRECTION_LOCALE, name = "locale"),
+            @EnumMap(value = TEXT_DIRECTION_ANY_RTL, name = "anyRtl"),
+            @EnumMap(value = TEXT_DIRECTION_LTR, name = "ltr"),
+            @EnumMap(value = TEXT_DIRECTION_RTL, name = "rtl"),
+            @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG, name = "firstStrong"),
+            @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name = "firstStrongLtr"),
+            @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name = "firstStrongRtl"),
+    })
     @UnsupportedAppUsage
     public int getRawTextDirection() {
         return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_MASK) >> PFLAG2_TEXT_DIRECTION_MASK_SHIFT;
@@ -25653,6 +25862,15 @@
             @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
     })
+    @InspectableProperty(hasAttributeId = false, enumMapping = {
+            @EnumMap(value = TEXT_DIRECTION_LOCALE, name = "locale"),
+            @EnumMap(value = TEXT_DIRECTION_ANY_RTL, name = "anyRtl"),
+            @EnumMap(value = TEXT_DIRECTION_LTR, name = "ltr"),
+            @EnumMap(value = TEXT_DIRECTION_RTL, name = "rtl"),
+            @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG, name = "firstStrong"),
+            @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_LTR, name = "firstStrongLtr"),
+            @EnumMap(value = TEXT_DIRECTION_FIRST_STRONG_RTL, name = "firstStrongRtl"),
+    })
     public int getTextDirection() {
         return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
     }
@@ -25824,6 +26042,15 @@
             @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
             @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
     })
+    @InspectableProperty(hasAttributeId = false, enumMapping = {
+            @EnumMap(value = TEXT_ALIGNMENT_INHERIT, name = "inherit"),
+            @EnumMap(value = TEXT_ALIGNMENT_GRAVITY, name = "gravity"),
+            @EnumMap(value = TEXT_ALIGNMENT_TEXT_START, name = "textStart"),
+            @EnumMap(value = TEXT_ALIGNMENT_TEXT_END, name = "textEnd"),
+            @EnumMap(value = TEXT_ALIGNMENT_CENTER, name = "center"),
+            @EnumMap(value = TEXT_ALIGNMENT_VIEW_START, name = "viewStart"),
+            @EnumMap(value = TEXT_ALIGNMENT_VIEW_END, name = "viewEnd")
+    })
     @TextAlignment
     @UnsupportedAppUsage
     public int getRawTextAlignment() {
@@ -25890,6 +26117,14 @@
             @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
             @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
     })
+    @InspectableProperty(enumMapping = {
+            @EnumMap(value = TEXT_ALIGNMENT_GRAVITY, name = "gravity"),
+            @EnumMap(value = TEXT_ALIGNMENT_TEXT_START, name = "textStart"),
+            @EnumMap(value = TEXT_ALIGNMENT_TEXT_END, name = "textEnd"),
+            @EnumMap(value = TEXT_ALIGNMENT_CENTER, name = "center"),
+            @EnumMap(value = TEXT_ALIGNMENT_VIEW_START, name = "viewStart"),
+            @EnumMap(value = TEXT_ALIGNMENT_VIEW_END, name = "viewEnd")
+    })
     @TextAlignment
     public int getTextAlignment() {
         return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK) >>
@@ -26122,6 +26357,7 @@
     /**
      * Gets the pointer icon for the current view.
      */
+    @InspectableProperty
     public PointerIcon getPointerIcon() {
         return mPointerIcon;
     }
@@ -26672,6 +26908,7 @@
      * if no name has been given.
      */
     @ViewDebug.ExportedProperty
+    @InspectableProperty
     public String getTransitionName() {
         return mTransitionName;
     }
@@ -28277,6 +28514,7 @@
      * @see #setTooltipText(CharSequence)
      * @attr ref android.R.styleable#View_tooltipText
      */
+    @InspectableProperty
     @Nullable
     public CharSequence getTooltipText() {
         return mTooltipInfo != null ? mTooltipInfo.mTooltipText : null;
diff --git a/core/java/android/view/inspector/InspectionCompanion.java b/core/java/android/view/inspector/InspectionCompanion.java
index ce0aee8..62d769b 100644
--- a/core/java/android/view/inspector/InspectionCompanion.java
+++ b/core/java/android/view/inspector/InspectionCompanion.java
@@ -62,7 +62,7 @@
      * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
      * called before {mapProperties}.
      *
-     * @param inspectable A object of type {@link T} to read the properties of.
+     * @param inspectable A object of type {T} to read the properties of.
      * @param propertyReader An object which receives the property IDs and values.
      */
     void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 8df83c0..797b861 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -76,7 +76,9 @@
             return new ActionsSuggestionsModel.ConversationMessage[]{
                     new ActionsSuggestionsModel.ConversationMessage(
                             FIRST_NON_LOCAL_USER,
-                            lastMessage.getText().toString())};
+                            lastMessage.getText().toString(),
+                            0,
+                            null)};
         }
 
         // Encode the messages in the reverse order, stop whenever the Person object is missing.
@@ -89,7 +91,7 @@
             }
             nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
                     personEncoder.encode(message.getAuthor()),
-                    message.getText().toString()));
+                    message.getText().toString(), 0, null));
         }
         return nativeMessages.toArray(
                 new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
diff --git a/core/java/android/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java
index b37e1b8..4d2c046 100644
--- a/core/java/android/webkit/TokenBindingService.java
+++ b/core/java/android/webkit/TokenBindingService.java
@@ -16,12 +16,7 @@
 
 package android.webkit;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.net.Uri;
-
-import java.security.KeyPair;
 
 /**
  * Enables the token binding procotol, and provides access to the keys. See
@@ -30,86 +25,9 @@
  * All methods are required to be called on the UI thread where WebView is
  * attached to the View hierarchy.
  * @hide
+ * @deprecated this is no longer supported.
  */
 @SystemApi
+@Deprecated
 public abstract class TokenBindingService {
-
-    public static final String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5";
-    public static final String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS";
-    public static final String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
-
-    /**
-     * Provides the KeyPair information.
-     */
-    public static abstract class TokenBindingKey {
-        /**
-         * The public, private key pair.
-         */
-        public abstract KeyPair getKeyPair();
-
-        /**
-         * The algorithm that is used to generate the key pair.
-         */
-        public abstract String getAlgorithm();
-    }
-
-    /**
-     * Returns the default TokenBinding service instance. At present there is
-     * only one token binding service instance for all WebView instances,
-     * however this restriction may be relaxed in the future.
-     *
-     * @return The default TokenBindingService instance.
-     */
-    public static TokenBindingService getInstance() {
-        return WebViewFactory.getProvider().getTokenBindingService();
-    }
-
-    /**
-     * Enables the token binding protocol. The token binding protocol
-     * has to be enabled before creating any WebViews.
-     *
-     * @throws IllegalStateException if a WebView was already created.
-     */
-    public abstract void enableTokenBinding();
-
-    /**
-     * Retrieves the key pair for a given origin from the internal
-     * TokenBinding key store asynchronously.
-     *
-     * The user can provide a list of acceptable algorithms for the retrieved
-     * key pair. If a key pair exists and it is in the list of algorithms, then
-     * the key is returned. If it is not in the list, no key is returned.
-     *
-     * If no key pair exists, WebView chooses an algorithm from the list, in
-     * the order given, to generate a key.
-     *
-     * The user can pass {@code null} if any algorithm is acceptable.
-     *
-     * @param origin The origin for the server.
-     * @param algorithm The list of algorithms. An IllegalArgumentException is thrown if array is
-     *                  empty.
-     * @param callback The callback that will be called when key is available.
-     */
-    public abstract void getKey(Uri origin,
-                                @Nullable String[] algorithm,
-                                @NonNull ValueCallback<TokenBindingKey> callback);
-    /**
-     * Deletes specified key (for use when associated cookie is cleared).
-     *
-     * @param origin The origin of the server.
-     * @param callback The callback that will be called when key is deleted. The
-     *        callback parameter (Boolean) will indicate if operation is
-     *        successful or if failed.
-     */
-    public abstract void deleteKey(Uri origin,
-                                   @Nullable ValueCallback<Boolean> callback);
-
-     /**
-      * Deletes all the keys (for use when cookies are cleared).
-      *
-      * @param callback The callback that will be called when keys are deleted.
-      *        The callback parameter (Boolean) will indicate if operation is
-      *        successful or if failed.
-      */
-    public abstract void deleteAllKeys(@Nullable ValueCallback<Boolean> callback);
 }
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 4ff49ea..6a1ed39 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -129,7 +129,8 @@
      * Gets the TokenBindingService instance for this WebView implementation. The
      * implementation must return the same instance on subsequent calls.
      *
-     * @return the TokenBindingService instance
+     * @deprecated this method only returns {@code null}
+     * @return the TokenBindingService instance (which is always {@code null})
      */
     TokenBindingService getTokenBindingService();
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c6155ce..3c32bb2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2051,8 +2051,8 @@
     }
 
     void updateCursorPosition() {
-        if (mTextView.mCursorDrawableRes == 0) {
-            mDrawableForCursor = null;
+        loadCursorDrawable();
+        if (mDrawableForCursor == null) {
             return;
         }
 
@@ -2462,10 +2462,7 @@
     }
 
     private void updateCursorPosition(int top, int bottom, float horizontal) {
-        if (mDrawableForCursor == null) {
-            mDrawableForCursor = mTextView.getContext().getDrawable(
-                    mTextView.mCursorDrawableRes);
-        }
+        loadCursorDrawable();
         final int left = clampHorizontalPosition(mDrawableForCursor, horizontal);
         final int width = mDrawableForCursor.getIntrinsicWidth();
         mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width,
@@ -5698,6 +5695,12 @@
         public boolean isActive();
     }
 
+    void loadCursorDrawable() {
+        if (mDrawableForCursor == null) {
+            mDrawableForCursor = mTextView.getTextCursorDrawable();
+        }
+    }
+
     private class InsertionPointCursorController implements CursorController {
         private InsertionHandleView mHandle;
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7b39efe..2dec4e8 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -18,6 +18,8 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DimenRes;
+import android.annotation.IntDef;
+import android.annotation.LayoutRes;
 import android.annotation.NonNull;
 import android.annotation.StyleRes;
 import android.annotation.UnsupportedAppUsage;
@@ -131,6 +133,12 @@
     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
 
     /**
+     * The intent extra that contains {@code true} if inflating as dak text theme.
+     * @hide
+     */
+    static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
+
+    /**
      * The intent extra that contains the bounds for all shared elements.
      */
     public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
@@ -163,6 +171,36 @@
     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
     private static final int SET_INT_TAG_TAG = 22;
 
+    /** @hide **/
+    @IntDef(flag = true, value = {
+            FLAG_REAPPLY_DISALLOWED,
+            FLAG_WIDGET_IS_COLLECTION_CHILD,
+            FLAG_USE_LIGHT_BACKGROUND_LAYOUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApplyFlags {}
+    /**
+     * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
+     * the layout in a way that isn't recoverable, since views are being removed.
+     * @hide
+     */
+    public static final int FLAG_REAPPLY_DISALLOWED = 1;
+    /**
+     * This flag indicates whether this RemoteViews object is being created from a
+     * RemoteViewsService for use as a child of a widget collection. This flag is used
+     * to determine whether or not certain features are available, in particular,
+     * setting on click extras and setting on click pending intents. The former is enabled,
+     * and the latter disabled when this flag is true.
+     * @hide
+     */
+    public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
+    /**
+     * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
+     * of {link #mLayoutId}
+     * @hide
+     */
+    public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
+
     /**
      * Application that hosts the remote views.
      *
@@ -178,6 +216,11 @@
     private final int mLayoutId;
 
     /**
+     * The resource ID of the layout file in dark text mode. (Added to the parcel)
+     */
+    private int mLightBackgroundLayoutId = 0;
+
+    /**
      * An array of actions to perform on the view tree once it has been
      * inflated
      */
@@ -197,12 +240,6 @@
     private boolean mIsRoot = true;
 
     /**
-     * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
-     * the layout in a way that isn't recoverable, since views are being removed.
-     */
-    private boolean mReapplyDisallowed;
-
-    /**
      * Constants to whether or not this RemoteViews is composed of a landscape and portrait
      * RemoteViews.
      */
@@ -218,14 +255,8 @@
     @UnsupportedAppUsage
     private RemoteViews mPortrait = null;
 
-    /**
-     * This flag indicates whether this RemoteViews object is being created from a
-     * RemoteViewsService for use as a child of a widget collection. This flag is used
-     * to determine whether or not certain features are available, in particular,
-     * setting on click extras and setting on click pending intents. The former is enabled,
-     * and the latter disabled when this flag is true.
-     */
-    private boolean mIsWidgetCollectionChild = false;
+    @ApplyFlags
+    private int mApplyFlags = 0;
 
     /** Class cookies of the Parcel this instance was read from. */
     private final Map<Class, Object> mClassCookies;
@@ -289,18 +320,15 @@
      *
      * @hide
      */
-    public void setReapplyDisallowed() {
-        mReapplyDisallowed = true;
+    public void addFlags(@ApplyFlags int flags) {
+        mApplyFlags = mApplyFlags | flags;
     }
 
     /**
-     * @return Whether it is disallowed to reapply another remoteview with the same layout as this
-     * view. True if this remoteview has actions that destroyed view tree of the base layout.
-     *
      * @hide
      */
-    public boolean isReapplyDisallowed() {
-        return mReapplyDisallowed;
+    public boolean hasFlags(@ApplyFlags int flag) {
+        return (mApplyFlags & flag) == flag;
     }
 
     /**
@@ -768,7 +796,10 @@
             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
             // RemoteViewsService
             AppWidgetHostView host = (AppWidgetHostView) rootParent;
-            intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
+            intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
+                    .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
+                            hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
+
             if (target instanceof AbsListView) {
                 AbsListView v = (AbsListView) target;
                 v.setRemoteViewsAdapter(intent, isAsync);
@@ -829,7 +860,7 @@
                 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
                 // much sense, do they mean to set a PendingIntent template for the
                 // AdapterView's children?
-                if (mIsWidgetCollectionChild) {
+                if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
                     Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
                             + "(id: " + viewId + ")");
                     ApplicationInfo appInfo = root.getContext().getApplicationInfo();
@@ -843,7 +874,7 @@
                 }
                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
             } else if (mResponse.mFillIntent != null) {
-                if (!mIsWidgetCollectionChild) {
+                if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
                     Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
                             + "only from RemoteViewsFactory (ie. on collection items).");
                     return;
@@ -1545,6 +1576,7 @@
             viewId = parcel.readInt();
             mIndex = parcel.readInt();
             mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
+            mNestedViews.addFlags(mApplyFlags);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
@@ -2190,7 +2222,7 @@
      *
      * @hide
      */
-    public RemoteViews(String packageName, int userId, int layoutId) {
+    public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
         this(getApplicationInfo(packageName, userId), layoutId);
     }
 
@@ -2203,7 +2235,7 @@
      *
      * @hide
      */
-    protected RemoteViews(ApplicationInfo application, int layoutId) {
+    protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
         mApplication = application;
         mLayoutId = layoutId;
         mBitmapCache = new BitmapCache();
@@ -2229,7 +2261,8 @@
             throw new RuntimeException("Both RemoteViews must share the same package and user");
         }
         mApplication = portrait.mApplication;
-        mLayoutId = portrait.getLayoutId();
+        mLayoutId = portrait.mLayoutId;
+        mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
 
         mLandscape = landscape;
         mPortrait = portrait;
@@ -2250,8 +2283,8 @@
         mApplication = src.mApplication;
         mIsRoot = src.mIsRoot;
         mLayoutId = src.mLayoutId;
-        mIsWidgetCollectionChild = src.mIsWidgetCollectionChild;
-        mReapplyDisallowed = src.mReapplyDisallowed;
+        mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
+        mApplyFlags = src.mApplyFlags;
         mClassCookies = src.mClassCookies;
 
         if (src.hasLandscapeAndPortraitLayouts()) {
@@ -2309,7 +2342,7 @@
             mApplication = parcel.readInt() == 0 ? info :
                     ApplicationInfo.CREATOR.createFromParcel(parcel);
             mLayoutId = parcel.readInt();
-            mIsWidgetCollectionChild = parcel.readInt() == 1;
+            mLightBackgroundLayoutId = parcel.readInt();
 
             readActionsFromParcel(parcel, depth);
         } else {
@@ -2318,9 +2351,10 @@
             mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
                     mClassCookies);
             mApplication = mPortrait.mApplication;
-            mLayoutId = mPortrait.getLayoutId();
+            mLayoutId = mPortrait.mLayoutId;
+            mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
         }
-        mReapplyDisallowed = parcel.readInt() == 0;
+        mApplyFlags = parcel.readInt();
     }
 
     private void readActionsFromParcel(Parcel parcel, int depth) {
@@ -2409,19 +2443,8 @@
      * @return the layout id.
      */
     public int getLayoutId() {
-        return mLayoutId;
-    }
-
-    /*
-     * This flag indicates whether this RemoteViews object is being created from a
-     * RemoteViewsService for use as a child of a widget collection. This flag is used
-     * to determine whether or not certain features are available, in particular,
-     * setting on click extras and setting on click pending intents. The former is enabled,
-     * and the latter disabled when this flag is true.
-     */
-    @UnsupportedAppUsage
-    void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
-        mIsWidgetCollectionChild = isWidgetCollectionChild;
+        return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
+                ? mLightBackgroundLayoutId : mLayoutId;
     }
 
     /**
@@ -3292,6 +3315,33 @@
         setInt(viewId, "setLabelFor", labeledId);
     }
 
+    /**
+     * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
+     * used by the host when the widgets displayed on a light-background where foreground elements
+     * and text can safely draw using a dark color without any additional background protection.
+     */
+    public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
+        mLightBackgroundLayoutId = layoutId;
+    }
+
+    /**
+     * If this view supports dark text versions, creates a copy representing that version,
+     * otherwise returns itself.
+     * @hide
+     */
+    public RemoteViews getDarkTextViews() {
+        if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
+            return this;
+        }
+
+        try {
+            addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+            return new RemoteViews(this);
+        } finally {
+            mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
+        }
+    }
+
     private RemoteViews getRemoteViewsToApply(Context context) {
         if (hasLandscapeAndPortraitLayouts()) {
             int orientation = context.getResources().getConfiguration().orientation;
@@ -3373,6 +3423,12 @@
      * @hide
      */
     public interface OnViewAppliedListener {
+        /**
+         * Callback when the RemoteView has finished inflating,
+         * but no actions have been applied yet.
+         */
+        default void onViewInflated(View v) {};
+
         void onViewApplied(View v);
 
         void onError(Exception e);
@@ -3469,6 +3525,10 @@
         @Override
         protected void onPostExecute(ViewTree viewTree) {
             if (mError == null) {
+                if (mListener != null) {
+                    mListener.onViewInflated(viewTree.mRoot);
+                }
+
                 try {
                     if (mActions != null) {
                         OnClickHandler handler = mHandler == null
@@ -3652,7 +3712,7 @@
                 mApplication.writeToParcel(dest, flags);
             }
             dest.writeInt(mLayoutId);
-            dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
+            dest.writeInt(mLightBackgroundLayoutId);
             writeActionsToParcel(dest);
         } else {
             dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
@@ -3665,7 +3725,7 @@
             // Both RemoteViews already share the same package and user
             mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
         }
-        dest.writeInt(mReapplyDisallowed ? 1 : 0);
+        dest.writeInt(mApplyFlags);
     }
 
     private void writeActionsToParcel(Parcel parcel) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index d17c7c5..c5cd1a1 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,6 +16,9 @@
 
 package android.widget;
 
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
+
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.app.IServiceConnection;
@@ -97,6 +100,7 @@
     private final Context mContext;
     private final Intent mIntent;
     private final int mAppWidgetId;
+    private final boolean mOnLightBackground;
     private final Executor mAsyncViewLoadExecutor;
 
     private OnClickHandler mRemoteViewsOnClickHandler;
@@ -817,13 +821,13 @@
             throw new IllegalArgumentException("Non-null Intent must be specified.");
         }
 
-        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+        mAppWidgetId = intent.getIntExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
         mRequestedViews = new RemoteViewsFrameLayoutRefSet();
+        mOnLightBackground = intent.getBooleanExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, false);
 
         // Strip the previously injected app widget id from service intent
-        if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
-            intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
-        }
+        intent.removeExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID);
+        intent.removeExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND);
 
         // Initialize the worker thread
         mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
@@ -1107,6 +1111,7 @@
             } else {
                 layout = new RemoteViewsFrameLayout(parent.getContext(), mCache);
                 layout.setExecutor(mAsyncViewLoadExecutor);
+                layout.setOnLightBackground(mOnLightBackground);
             }
 
             if (isInCache) {
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index e490458..b80fe48 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -85,7 +85,7 @@
     public View getView(int position, View convertView, ViewGroup parent) {
         if (position < getCount()) {
             RemoteViews rv = mRemoteViewsList.get(position);
-            rv.setIsWidgetCollectionChild(true);
+            rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
             View v;
             if (convertView != null && rv != null &&
                     convertView.getId() == rv.getLayoutId()) {
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 2827f63..214e5cc 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -163,7 +163,7 @@
             try {
                 rv = mFactory.getViewAt(position);
                 if (rv != null) {
-                    rv.setIsWidgetCollectionChild(true);
+                    rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
                 }
             } catch (Exception ex) {
                 Thread t = Thread.currentThread();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 085f8f1..90cf871 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -799,8 +799,9 @@
 
     // Although these fields are specific to editable text, they are not added to Editor because
     // they are defined by the TextView's style and are theme-dependent.
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     int mCursorDrawableRes;
+    private Drawable mCursorDrawable;
     // Note: this might be stale if setTextSelectHandleLeft is used. We could simplify the code
     // by removing it, but we would break apps targeting <= P that use it by reflection.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -3640,6 +3641,8 @@
     /**
      * Returns the Drawable corresponding to the right handle used
      * for selecting text.
+     * Note that any change applied to the handle Drawable will not be visible
+     * until the handle is hidden and then drawn again.
      *
      * @return the right text selection handle drawable
      *
@@ -3655,6 +3658,58 @@
     }
 
     /**
+     * Sets the Drawable corresponding to the text cursor. The Drawable defaults to the
+     * value of the textCursorDrawable attribute.
+     * Note that any change applied to the cursor Drawable will not be visible
+     * until the cursor is hidden and then drawn again.
+     *
+     * @see #setTextCursorDrawable(int)
+     * @attr ref android.R.styleable#TextView_textCursorDrawable
+     */
+    public void setTextCursorDrawable(@NonNull Drawable textCursorDrawable) {
+        Preconditions.checkNotNull(textCursorDrawable,
+                "The cursor drawable should not be null.");
+        mCursorDrawable = textCursorDrawable;
+        mCursorDrawableRes = 0;
+        if (mEditor != null) {
+            mEditor.loadCursorDrawable();
+        }
+    }
+
+    /**
+     * Sets the Drawable corresponding to the text cursor. The Drawable defaults to the
+     * value of the textCursorDrawable attribute.
+     * Note that any change applied to the cursor Drawable will not be visible
+     * until the cursor is hidden and then drawn again.
+     *
+     * @see #setTextCursorDrawable(Drawable)
+     * @attr ref android.R.styleable#TextView_textCursorDrawable
+     */
+    public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) {
+        Preconditions.checkArgumentPositive(textCursorDrawable,
+                "The cursor drawable should be a valid drawable resource id.");
+        setTextCursorDrawable(mContext.getDrawable(textCursorDrawable));
+    }
+
+    /**
+     * Returns the Drawable corresponding to the text cursor.
+     * Note that any change applied to the cursor Drawable will not be visible
+     * until the cursor is hidden and then drawn again.
+     *
+     * @return the text cursor drawable
+     *
+     * @see #setTextCursorDrawable(Drawable)
+     * @see #setTextCursorDrawable(int)
+     * @attr ref android.R.styleable#TextView_textCursorDrawable
+     */
+    @Nullable public Drawable getTextCursorDrawable() {
+        if (mCursorDrawable == null && mCursorDrawableRes != 0) {
+            mCursorDrawable = mContext.getDrawable(mCursorDrawableRes);
+        }
+        return mCursorDrawable;
+    }
+
+    /**
      * Sets the text appearance from the specified style resource.
      * <p>
      * Use a framework-defined {@code TextAppearance} style like
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 875d7c9..ff34036 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Binder;
+import android.os.Process;
 import android.os.SystemClock;
+import android.os.ThreadLocalWorkSource;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
@@ -78,8 +80,10 @@
     private final Random mRandom;
     private long mStartTime = System.currentTimeMillis();
     private long mCallStatsCount = 0;
+    private boolean mAddDebugEntries = false;
 
     private CachedDeviceState.Readonly mDeviceState;
+    private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
 
     /** Injector for {@link BinderCallsStats}. */
     public static class Injector {
@@ -93,7 +97,11 @@
     }
 
     public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+        if (mBatteryStopwatch != null) {
+            mBatteryStopwatch.close();
+        }
         mDeviceState = deviceState;
+        mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
     }
 
     @Override
@@ -155,8 +163,7 @@
                 return;
             }
 
-            final boolean isWorkSourceSet = workSourceUid >= 0;
-            final UidEntry uidEntry = getUidEntry(isWorkSourceSet ? workSourceUid : callingUid);
+            final UidEntry uidEntry = getUidEntry(workSourceUid);
             uidEntry.callCount++;
 
             if (recordCall) {
@@ -317,9 +324,30 @@
             exported.methodName = methodName;
         }
 
+        // Debug entries added to help validate the data.
+        if (mAddDebugEntries && mBatteryStopwatch != null) {
+            resultCallStats.add(createDebugEntry("start_time_millis", mStartTime));
+            resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+            resultCallStats.add(
+                    createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
+        }
+
         return resultCallStats;
     }
 
+    private ExportedCallStat createDebugEntry(String variableName, long value) {
+        final int uid = Process.myUid();
+        final ExportedCallStat callStat = new ExportedCallStat();
+        callStat.className = "";
+        callStat.workSourceUid = uid;
+        callStat.callingUid = uid;
+        callStat.recordedCallCount = 1;
+        callStat.callCount = 1;
+        callStat.methodName = "__DEBUG_" + variableName;
+        callStat.latencyMicros = value;
+        return callStat;
+    }
+
     /** @hide */
     public ArrayMap<String, Integer> getExportedExceptionStats() {
         synchronized (mLock) {
@@ -341,6 +369,8 @@
         long totalCpuTime = 0;
         pw.print("Start time: ");
         pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+        pw.print("On battery time (ms): ");
+        pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
         pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
         final List<UidEntry> entries = new ArrayList<>();
 
@@ -434,7 +464,7 @@
     }
 
     protected int getWorkSourceUid() {
-        return Binder.getCallingWorkSourceUid();
+        return ThreadLocalWorkSource.getUid();
     }
 
     protected long getElapsedRealtimeMicro() {
@@ -457,6 +487,10 @@
         }
     }
 
+    public void setAddDebugEntries(boolean addDebugEntries) {
+        mAddDebugEntries = addDebugEntries;
+    }
+
     /**
      * Sets the maximum number of items to track.
      */
@@ -496,6 +530,9 @@
             mUidEntries.clear();
             mExceptionCounts.clear();
             mStartTime = System.currentTimeMillis();
+            if (mBatteryStopwatch != null) {
+                mBatteryStopwatch.reset();
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
index 8c90682..334cca3 100644
--- a/core/java/com/android/internal/os/CachedDeviceState.java
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -16,8 +16,14 @@
 
 package com.android.internal.os;
 
+
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
+
 /**
  * Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
  * the System Server telemetry services.
@@ -27,6 +33,9 @@
 public class CachedDeviceState {
     private volatile boolean mScreenInteractive;
     private volatile boolean mCharging;
+    private final Object mStopwatchesLock = new Object();
+    @GuardedBy("mStopwatchLock")
+    private final ArrayList<TimeInStateStopwatch> mOnBatteryStopwatches = new ArrayList<>();
 
     public CachedDeviceState() {
         mCharging = true;
@@ -44,7 +53,23 @@
     }
 
     public void setCharging(boolean charging) {
-        mCharging = charging;
+        if (mCharging != charging) {
+            mCharging = charging;
+            updateStopwatches(/* shouldStart= */ !charging);
+        }
+    }
+
+    private void updateStopwatches(boolean shouldStart) {
+        synchronized (mStopwatchesLock) {
+            final int size = mOnBatteryStopwatches.size();
+            for (int i = 0; i < size; i++) {
+                if (shouldStart) {
+                    mOnBatteryStopwatches.get(i).start();
+                } else {
+                    mOnBatteryStopwatches.get(i).stop();
+                }
+            }
+        }
     }
 
     public Readonly getReadonlyClient() {
@@ -62,5 +87,74 @@
         public boolean isScreenInteractive() {
             return mScreenInteractive;
         }
+
+        /** Creates a {@link TimeInStateStopwatch stopwatch} that tracks the time on battery. */
+        public TimeInStateStopwatch createTimeOnBatteryStopwatch() {
+            synchronized (mStopwatchesLock) {
+                final TimeInStateStopwatch stopwatch = new TimeInStateStopwatch();
+                mOnBatteryStopwatches.add(stopwatch);
+                if (!mCharging) {
+                    stopwatch.start();
+                }
+                return stopwatch;
+            }
+        }
+    }
+
+    /** Tracks the time the device spent in a given state. */
+    public class TimeInStateStopwatch implements AutoCloseable {
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private long mStartTimeMillis;
+        @GuardedBy("mLock")
+        private long mTotalTimeMillis;
+
+        /** Returns the time in state since the last call to {@link TimeInStateStopwatch#reset}. */
+        public long getMillis() {
+            synchronized (mLock) {
+                return mTotalTimeMillis + elapsedTime();
+            }
+        }
+
+        /** Resets the time in state to 0 without stopping the timer if it's started. */
+        public void reset() {
+            synchronized (mLock) {
+                mTotalTimeMillis = 0;
+                mStartTimeMillis = isRunning() ? SystemClock.elapsedRealtime() : 0;
+            }
+        }
+
+        private void start() {
+            synchronized (mLock) {
+                if (!isRunning()) {
+                    mStartTimeMillis = SystemClock.elapsedRealtime();
+                }
+            }
+        }
+
+        private void stop() {
+            synchronized (mLock) {
+                if (isRunning()) {
+                    mTotalTimeMillis += elapsedTime();
+                    mStartTimeMillis = 0;
+                }
+            }
+        }
+
+        private long elapsedTime() {
+            return isRunning() ? SystemClock.elapsedRealtime() - mStartTimeMillis : 0;
+        }
+
+        @VisibleForTesting
+        public boolean isRunning() {
+            return mStartTimeMillis > 0;
+        }
+
+        @Override
+        public void close() {
+            synchronized (mStopwatchesLock) {
+                mOnBatteryStopwatches.remove(this);
+            }
+        }
     }
 }
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index cf2a297..01fd8ba 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -49,6 +49,7 @@
     private final int mEntriesSizeCap;
     private int mSamplingInterval;
     private CachedDeviceState.Readonly mDeviceState;
+    private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
     private long mStartTime = System.currentTimeMillis();
     private boolean mAddDebugEntries = false;
 
@@ -58,7 +59,12 @@
     }
 
     public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+        if (mBatteryStopwatch != null) {
+            mBatteryStopwatch.close();
+        }
+
         mDeviceState = deviceState;
+        mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
     }
 
     public void setAddDebugEntries(boolean addDebugEntries) {
@@ -148,9 +154,11 @@
         maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
         maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
         // Debug entries added to help validate the data.
-        if (mAddDebugEntries) {
+        if (mAddDebugEntries && mBatteryStopwatch != null) {
             exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
             exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+            exportedEntries.add(
+                    createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
         }
         return exportedEntries;
     }
@@ -159,7 +167,7 @@
         final Entry entry = new Entry("__DEBUG_" + variableName);
         entry.messageCount = 1;
         entry.recordedMessageCount = 1;
-        entry.maxDelayMillis = value;
+        entry.totalLatencyMicro = value;
         return new ExportedEntry(entry);
     }
 
@@ -168,6 +176,10 @@
         return mStartTime;
     }
 
+    public long getBatteryTimeMillis() {
+        return mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0;
+    }
+
     private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
         synchronized (specialEntry) {
             if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
@@ -188,6 +200,9 @@
             mOverflowEntry.reset();
         }
         mStartTime = System.currentTimeMillis();
+        if (mBatteryStopwatch != null) {
+            mBatteryStopwatch.reset();
+        }
     }
 
     public void setSamplingInterval(int samplingInterval) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 600b1b3..53b56f2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -29,7 +29,7 @@
 {
     void setIcon(String slot, in StatusBarIcon icon);
     void removeIcon(String slot);
-    void disable(int state1, int state2);
+    void disable(int displayId, int state1, int state2);
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel(String subPanel);
     void animateCollapsePanels();
@@ -38,8 +38,9 @@
     void showWirelessChargingAnimation(int batteryLevel);
 
     /**
-     * Notifies the status bar of a System UI visibility flag change.
+     * Notifies System UI side of a visibility flag change on the specified display.
      *
+     * @param displayId the id of the display to notify
      * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
      *            separately in fullscreenStackVis and dockedStackVis
      * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
@@ -50,13 +51,13 @@
      * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
      * @param dockedBounds the current bounds of the docked stack, in screen coordinates
      */
-    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
-            in Rect fullscreenBounds, in Rect dockedBounds);
+    void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, in Rect fullscreenBounds, in Rect dockedBounds);
 
-    void topAppWindowChanged(boolean menuVisible);
-    void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+    void topAppWindowChanged(int displayId, boolean menuVisible);
+    void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
-    void setWindowState(int window, int state);
+    void setWindowState(int display, int window, int state);
 
     void showRecentApps(boolean triggeredFromAltTab);
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
@@ -70,30 +71,38 @@
     void toggleKeyboardShortcutsMenu(int deviceId);
 
     /**
-     * Notifies the status bar that an app transition is pending to delay applying some flags with
-     * visual impact until {@link #appTransitionReady} is called.
-     */
-    void appTransitionPending();
-
-    /**
-     * Notifies the status bar that a pending app transition has been cancelled.
-     */
-    void appTransitionCancelled();
-
-    /**
-     * Notifies the status bar that an app transition is now being executed.
+     * Notifies System UI on the specified display that an app transition is pending to delay
+     * applying some flags with visual impact until {@link #appTransitionReady} is called.
      *
+     * @param displayId the id of the display to notify
+     */
+    void appTransitionPending(int displayId);
+
+    /**
+     * Notifies System UI on the specified display that a pending app transition has been cancelled.
+     *
+     * @param displayId the id of the display to notify
+     */
+    void appTransitionCancelled(int displayId);
+
+    /**
+     * Notifies System UI on the specified display that an app transition is now being executed.
+     *
+     * @param displayId the id of the display to notify
      * @param statusBarAnimationsStartTime the desired start time for all visual animations in the
      *        status bar caused by this app transition in uptime millis
      * @param statusBarAnimationsDuration the duration for all visual animations in the status
      *        bar caused by this app transition in millis
      */
-    void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+    void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
+            long statusBarAnimationsDuration);
 
     /**
-     * Notifies the status bar that an app transition is done.
+     * Notifies System UI on the specified display that an app transition is done.
+     *
+     * @param displayId the id of the display to notify
      */
-    void appTransitionFinished();
+    void appTransitionFinished(int displayId);
 
     void showAssistDisclosure();
     void startAssist(in Bundle args);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bf82dc61..5118e5f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -41,6 +41,7 @@
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
+    // TODO(b/117478341): support back button change when IME is showing on a external display.
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
     void expandSettingsPanel(String subPanel);
@@ -69,7 +70,7 @@
     void onNotificationSmartRepliesAdded(in String key, in int replyCount);
     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);
+    void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
 
     void onGlobalActionsShown();
     void onGlobalActionsHidden();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 4b66267..f669e94 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -32,6 +32,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.IntFunction;
 
 /**
  * ArrayUtils contains some methods that you can call to find out
@@ -656,4 +657,30 @@
             throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
         }
     }
+
+    /**
+     * Returns an array with values from {@code val} minus {@code null} values
+     *
+     * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
+     */
+    public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) {
+        int nullCount = 0;
+        int size = size(val);
+        for (int i = 0; i < size; i++) {
+            if (val[i] == null) {
+                nullCount++;
+            }
+        }
+        if (nullCount == 0) {
+            return val;
+        }
+        T[] result = arrayConstructor.apply(size - nullCount);
+        int outIdx = 0;
+        for (int i = 0; i < size; i++) {
+            if (val[i] != null) {
+                result[outIdx++] = val[i];
+            }
+        }
+        return result;
+    }
 }
diff --git a/core/java/com/android/internal/widget/ImageMessageConsumer.java b/core/java/com/android/internal/widget/ImageMessageConsumer.java
new file mode 100644
index 0000000..01613dc
--- /dev/null
+++ b/core/java/com/android/internal/widget/ImageMessageConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.widget;
+
+/**
+ * An interface for the class who will use the {@link ImageResolver} to resolve images.
+ */
+public interface ImageMessageConsumer {
+    /**
+     * Set the custom {@link ImageResolver} other than {@link LocalImageResolver}.
+     * @param resolver An image resolver that has custom implementation.
+     */
+    void setImageResolver(ImageResolver resolver);
+}
diff --git a/core/java/com/android/internal/widget/ImageResolver.java b/core/java/com/android/internal/widget/ImageResolver.java
new file mode 100644
index 0000000..4588525
--- /dev/null
+++ b/core/java/com/android/internal/widget/ImageResolver.java
@@ -0,0 +1,32 @@
+/*
+ * 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.widget;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+/**
+ * An interface for image resolvers that have custom implementations like cache mechanisms.
+ */
+public interface ImageResolver {
+    /**
+     * Load an image from specified uri.
+     * @param uri Uri of the target image.
+     * @return Target image in Drawable.
+     */
+    Drawable loadImage(Uri uri);
+}
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index 71d3bb5..2302de2 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -17,7 +17,6 @@
 package com.android.internal.widget;
 
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 607a3a9..64650a7 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -25,6 +25,7 @@
 import android.graphics.Canvas;
 import android.graphics.Path;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pools;
@@ -57,6 +58,7 @@
     private int mActualWidth;
     private int mActualHeight;
     private boolean mIsIsolated;
+    private ImageResolver mImageResolver;
 
     public MessagingImageMessage(@NonNull Context context) {
         this(context, null);
@@ -96,11 +98,16 @@
         MessagingMessage.super.setMessage(message);
         Drawable drawable;
         try {
-            drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext());
+            Uri uri = message.getDataUri();
+            drawable = mImageResolver != null ? mImageResolver.loadImage(uri) :
+                    LocalImageResolver.resolveImage(uri, getContext());
         } catch (IOException | SecurityException e) {
             e.printStackTrace();
             return false;
         }
+        if (drawable == null) {
+            return false;
+        }
         int intrinsicHeight = drawable.getIntrinsicHeight();
         if (intrinsicHeight == 0) {
             Log.w(TAG, "Drawable with 0 intrinsic height was returned");
@@ -114,7 +121,7 @@
     }
 
     static MessagingMessage createMessage(MessagingLayout layout,
-            Notification.MessagingStyle.Message m) {
+            Notification.MessagingStyle.Message m, ImageResolver resolver) {
         MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
         MessagingImageMessage createdMessage = sInstancePool.acquire();
         if (createdMessage == null) {
@@ -125,6 +132,7 @@
                             false);
             createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
         }
+        createdMessage.setImageResolver(resolver);
         boolean created = createdMessage.setMessage(m);
         if (!created) {
             createdMessage.recycle();
@@ -133,6 +141,10 @@
         return createdMessage;
     }
 
+    private void setImageResolver(ImageResolver resolver) {
+        mImageResolver = resolver;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         canvas.save();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 0f2e9c5..07d0d7d 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -57,7 +57,7 @@
  * messages and adapts the layout accordingly.
  */
 @RemoteViews.RemoteView
-public class MessagingLayout extends FrameLayout {
+public class MessagingLayout extends FrameLayout implements ImageMessageConsumer {
 
     private static final float COLOR_SHIFT_AMOUNT = 60;
     /**
@@ -95,6 +95,7 @@
     private Person mUser;
     private CharSequence mNameReplacement;
     private boolean mDisplayImagesAtEnd;
+    private ImageResolver mImageResolver;
 
     public MessagingLayout(@NonNull Context context) {
         super(context);
@@ -167,6 +168,11 @@
         bind(newMessages, newHistoricMessages, showSpinner);
     }
 
+    @Override
+    public void setImageResolver(ImageResolver resolver) {
+        mImageResolver = resolver;
+    }
+
     private void addRemoteInputHistoryToMessages(
             List<Notification.MessagingStyle.Message> newMessages,
             CharSequence[] remoteInputHistory) {
@@ -463,12 +469,12 @@
      */
     private List<MessagingMessage> createMessages(
             List<Notification.MessagingStyle.Message> newMessages, boolean historic) {
-        List<MessagingMessage> result = new ArrayList<>();;
+        List<MessagingMessage> result = new ArrayList<>();
         for (int i = 0; i < newMessages.size(); i++) {
             Notification.MessagingStyle.Message m = newMessages.get(i);
             MessagingMessage message = findAndRemoveMatchingMessage(m);
             if (message == null) {
-                message = MessagingMessage.createMessage(this, m);
+                message = MessagingMessage.createMessage(this, m, mImageResolver);
             }
             message.setIsHistoric(historic);
             result.add(message);
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index 74d0aae..c32d370 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -33,9 +33,9 @@
     String IMAGE_MIME_TYPE_PREFIX = "image/";
 
     static MessagingMessage createMessage(MessagingLayout layout,
-            Notification.MessagingStyle.Message m) {
+            Notification.MessagingStyle.Message m, ImageResolver resolver) {
         if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) {
-            return MessagingImageMessage.createMessage(layout, m);
+            return MessagingImageMessage.createMessage(layout, m, resolver);
         } else {
             return MessagingTextMessage.createMessage(layout, m);
         }
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index dcb7874..cfe742d 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -59,7 +59,7 @@
 static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
 {
     ALOG_ASSERT(codepoint <= 0xFFFF);
-    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    paint->setTextEncoding(kGlyphID_SkTextEncoding);
 
     SkScalar skWidth;
     SkRect skBounds;
@@ -84,7 +84,7 @@
 {
     HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
     SkPaint* paint = hbFontData->m_paint;
-    paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
+    paint->setTextEncoding(kUTF32_SkTextEncoding);
 
     if (unicode > 0x10ffff) {
         unicode = 0xfffd;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a8b0640..c249e20 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -71,7 +71,7 @@
 
 static void defaultSettingsForAndroid(Paint* paint) {
     // GlyphID encoding is required because we are using Harfbuzz shaping
-    paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+    paint->setTextEncoding(kGlyphID_SkTextEncoding);
 }
 
 namespace PaintGlue {
@@ -321,7 +321,7 @@
         x += MinikinUtils::xOffsetForTextAlign(paint, layout);
         Paint::Align align = paint->getTextAlign();
         paint->setTextAlign(Paint::kLeft_Align);
-        paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+        paint->setTextEncoding(kGlyphID_SkTextEncoding);
         GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
         MinikinUtils::forFontRun(layout, paint, f);
         paint->setTextAlign(align);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8e9a0bf..0286730 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -102,6 +102,11 @@
   MOUNT_EXTERNAL_FULL = 4,
 };
 
+// Must match values in com.android.internal.os.Zygote.
+enum RuntimeFlags : uint32_t {
+  DEBUG_ENABLE_JDWP = 1,
+};
+
 static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
   std::ostringstream oss;
   oss << __FILE__ << ":" << line << ": " << msg;
@@ -254,6 +259,36 @@
   return true;
 }
 
+static void EnableDebugger() {
+  // To let a non-privileged gdbserver attach to this
+  // process, we must set our dumpable flag.
+  if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+    ALOGE("prctl(PR_SET_DUMPABLE) failed");
+  }
+
+  // A non-privileged native debugger should be able to attach to the debuggable app, even if Yama
+  // is enabled (see kernel/Documentation/security/Yama.txt).
+  if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == -1) {
+    // if Yama is off prctl(PR_SET_PTRACER) returns EINVAL - don't log in this
+    // case since it's expected behaviour.
+    if (errno != EINVAL) {
+      ALOGE("prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) failed");
+    }
+  }
+
+  // We don't want core dumps, though, so set the soft limit on core dump size
+  // to 0 without changing the hard limit.
+  rlimit rl;
+  if (getrlimit(RLIMIT_CORE, &rl) == -1) {
+    ALOGE("getrlimit(RLIMIT_CORE) failed");
+  } else {
+    rl.rlim_cur = 0;
+    if (setrlimit(RLIMIT_CORE, &rl) == -1) {
+      ALOGE("setrlimit(RLIMIT_CORE) failed");
+    }
+  }
+}
+
 // The debug malloc library needs to know whether it's the zygote or a child.
 extern "C" int gMallocLeakZygoteChild;
 
@@ -956,6 +991,11 @@
     }
   }
 
+  // Set process properties to enable debugging if required.
+  if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
+    EnableDebugger();
+  }
+
   if (NeedsNoRandomizeWorkaround()) {
     // Work around ARM kernel ASLR lossage (http://b/5817320).
     int old_personality = personality(0xffffffff);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3a908dc..8e4eb00 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -38,6 +38,12 @@
     // Unknown page. Should not be used in production code.
     PAGE_UNKNOWN = 0;
 
+    // OPEN: Settings > Connected Devices > Bluetooth > (click on details link for a paired device)
+    BLUETOOTH_DEVICE_DETAILS = 1009;
+
+    // OPEN: Settings > Connected devices > Bluetooth > Pair new device
+    BLUETOOTH_PAIRING = 1018;
+
     // OPEN: Settings homepage
     SETTINGS_HOMEPAGE = 1502;
 
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d917536..4c1fc5c 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -761,6 +761,13 @@
     }
     optional SmartSelection smart_selection = 108;
 
+    message SmartSuggestions {
+      option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+      optional SettingProto service_explicitly_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional SmartSuggestions smart_suggestions = 145;
+
     message Sms {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -869,6 +876,8 @@
         // Temperature at which the high temperature warning notification should
         // be shown.
         optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // USB temperature at which the high temperature alarm notification should be shown.
+        optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional TemperatureWarning temperature_warning = 119;
 
@@ -991,5 +1000,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 145;
+    // Next tag = 146;
 }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ab50ad1..60561bd 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -185,6 +185,7 @@
     optional int32 app_id = 5;
     optional int32 isolated_app_id = 6;
     optional bool persistent = 7;
+    optional int32 lru_index = 8;
 }
 
 message BroadcastRecordProto {
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index e83a2bf..231caab 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -215,6 +215,34 @@
     // The fraction of a prefetch job's running window that must pass before
     // we consider matching it against a metered network.
     optional double conn_prefetch_relax_frac = 22;
+    // Whether to use heartbeats or rolling window for quota management. True
+    // will use heartbeats, false will use a rolling window.
+    optional bool use_heartbeats = 23;
+
+    message QuotaController {
+        // How much time each app will have to run jobs within their standby bucket window.
+        optional int64 allowed_time_per_period_ms = 1;
+        // How much time the package should have before transitioning from out-of-quota to in-quota.
+        // This should not affect processing if the package is already in-quota.
+        optional int64 in_quota_buffer_ms = 2;
+        // The quota window size of the particular standby bucket. Apps in this standby bucket are
+        // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+        // WINDOW_SIZE_MS.
+        optional int64 active_window_size_ms = 3;
+        // The quota window size of the particular standby bucket. Apps in this standby bucket are
+        // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+        // WINDOW_SIZE_MS.
+        optional int64 working_window_size_ms = 4;
+        // The quota window size of the particular standby bucket. Apps in this standby bucket are
+        // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+        // WINDOW_SIZE_MS.
+        optional int64 frequent_window_size_ms = 5;
+        // The quota window size of the particular standby bucket. Apps in this standby bucket are
+        // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+        // WINDOW_SIZE_MS.
+        optional int64 rare_window_size_ms = 6;
+    }
+    optional QuotaController quota_controller = 24;
 }
 
 message StateControllerProto {
@@ -357,6 +385,65 @@
         }
         repeated TrackedJob tracked_jobs = 2;
     }
+    message QuotaController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+        optional bool is_charging = 1;
+        optional bool is_in_parole = 2;
+
+        message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+            optional JobStatusDumpProto.Bucket effective_standby_bucket = 3;
+            optional bool has_quota = 4;
+            // The amount of time that this job has remaining in its quota. This
+            // can be negative if the job is out of quota.
+            optional int64 remaining_quota_ms = 5;
+        }
+        repeated TrackedJob tracked_jobs = 3;
+
+        message Package {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional int32 user_id = 1;
+            optional string name = 2;
+        }
+
+        message TimingSession {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional int64 start_time_elapsed = 1;
+            optional int64 end_time_elapsed = 2;
+            optional int32 job_count = 3;
+        }
+
+        message Timer {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional Package pkg = 1;
+            // True if the Timer is actively tracking jobs.
+            optional bool is_active = 2;
+            // The time this timer last became active. Only valid if is_active is true.
+            optional int64 start_time_elapsed = 3;
+            // How many are currently running. Valid only if the device is_active is true.
+            optional int32 job_count = 4;
+            // All of the jobs that the Timer is currently tracking.
+            repeated JobStatusShortInfoProto running_jobs = 5;
+        }
+
+        message PackageStats {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional Package pkg = 1;
+
+            optional Timer timer = 2;
+
+            repeated TimingSession saved_sessions = 3;
+        }
+        repeated PackageStats package_stats = 4;
+    }
     message StorageController {
         option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -403,8 +490,10 @@
         ContentObserverController content_observer = 4;
         DeviceIdleJobsController device_idle = 5;
         IdleController idle = 6;
+        QuotaController quota = 9;
         StorageController storage = 7;
         TimeController time = 8;
+        // Next tag: 10
     }
 }
 
@@ -603,11 +692,13 @@
         CONSTRAINT_CONNECTIVITY = 7;
         CONSTRAINT_CONTENT_TRIGGER = 8;
         CONSTRAINT_DEVICE_NOT_DOZING = 9;
+        CONSTRAINT_WITHIN_QUOTA = 10;
     }
     repeated Constraint required_constraints = 7;
     repeated Constraint satisfied_constraints = 8;
     repeated Constraint unsatisfied_constraints = 9;
     optional bool is_doze_whitelisted = 10;
+    optional bool is_uid_active = 26;
 
     message ImplicitConstraints {
         // The device isn't Dozing or this job will be in the foreground. This
@@ -627,6 +718,7 @@
         TRACKING_IDLE = 3;
         TRACKING_STORAGE = 4;
         TRACKING_TIME = 5;
+        TRACKING_QUOTA = 6;
     }
     // Controllers that are currently tracking the job.
     repeated TrackingController tracking_controllers = 11;
@@ -660,6 +752,7 @@
         NEVER = 4;
     }
     optional Bucket standby_bucket = 17;
+    optional bool is_exempted_from_app_standby = 27;
 
     optional int64 enqueue_duration_ms = 18;
     // Can be negative if the earliest runtime deadline has passed.
@@ -674,5 +767,5 @@
 
     optional int64 internal_flags = 24;
 
-    // Next tag: 26
+    // Next tag: 28
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 679a1d2..3767ed5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -211,7 +211,7 @@
     repeated AppWindowTokenProto app_window_tokens = 3;
     optional bool fills_parent = 4;
     optional .android.graphics.RectProto bounds = 5;
-    optional .android.graphics.RectProto temp_inset_bounds = 6;
+    optional .android.graphics.RectProto displayed_bounds = 6;
     optional bool defer_removal = 7;
     optional int32 surface_width = 8;
     optional int32 surface_height = 9;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 83f3057..3018614 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3387,6 +3387,13 @@
     <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to control display color transformations.
+         <p>Not for use by third-party applications.</p>
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to collect usage infomation about brightness slider changes.
          <p>Not for use by third-party applications.</p>
          @hide
@@ -4170,6 +4177,11 @@
     <permission android:name="android.permission.MANAGE_AUTO_FILL"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the smart suggestions service.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS"
+        android:protectionLevel="signature" />
+
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
          @hide  <p>Not for use by third-party applications.</p> -->
diff --git a/core/res/res/layout/notification_material_reply_text.xml b/core/res/res/layout/notification_material_reply_text.xml
index 71632a2..2b15dce 100644
--- a/core/res/res/layout/notification_material_reply_text.xml
+++ b/core/res/res/layout/notification_material_reply_text.xml
@@ -39,7 +39,7 @@
             android:layout_height="wrap_content"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:visibility="gone"
-            android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+            android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
             android:singleLine="true" />
 
     <TextView
@@ -48,7 +48,7 @@
             android:layout_height="wrap_content"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:visibility="gone"
-            android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+            android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
             android:singleLine="true" />
 
     <LinearLayout
@@ -64,7 +64,7 @@
                 android:layout_weight="1"
                 android:layout_marginEnd="@dimen/notification_content_margin_end"
                 android:layout_gravity="center"
-                android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
+                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
                 android:singleLine="true" />
         <ProgressBar
             android:id="@+id/notification_material_reply_progress"
diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml
index c00acd5..be5d9b4 100644
--- a/core/res/res/layout/notification_template_ambient_header.xml
+++ b/core/res/res/layout/notification_template_ambient_header.xml
@@ -24,5 +24,5 @@
     android:layout_height="wrap_content">
     <include
         layout="@layout/notification_template_header"
-        android:theme="@style/Theme.Material.Notification.Ambient"/>
+        android:theme="@style/Theme.DeviceDefault.Notification.Ambient"/>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 4bf1ad6..5ba1cf2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,7 +17,7 @@
 <!-- extends ViewGroup -->
 <NotificationHeaderView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:theme="@style/Theme.Material.Notification"
+    android:theme="@style/Theme.DeviceDefault.Notification"
     android:id="@+id/notification_header"
     android:orientation="horizontal"
     android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index c8864c2..2c6064e 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -24,7 +24,7 @@
     android:paddingEnd="@dimen/notification_extra_margin_ambient"
     >
     <include layout="@layout/notification_template_ambient_header"
-             android:theme="@style/Theme.Material.Notification.Ambient" />
+             android:theme="@style/Theme.DeviceDefault.Notification.Ambient" />
 
     <LinearLayout
             android:id="@+id/notification_action_list_margin_target"
@@ -51,8 +51,7 @@
             android:orientation="vertical"
             >
             <TextView android:id="@+id/title"
-                android:textAppearance="@style/TextAppearance.Material.Notification.Title"
-                android:fontFamily="sans-serif"
+                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:gravity="top|center_horizontal"
@@ -65,7 +64,7 @@
             <TextView android:id="@+id/text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
                 android:singleLine="false"
                 android:layout_weight="1"
                 android:gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 26eb4e7..d5ea96f 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -53,7 +53,7 @@
                 android:layout_marginTop="@dimen/notification_progress_margin_top"
                 android:layout_marginBottom="6dp"/>
             <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/notification_text_margin_top"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index d6f0e1d..eb89258 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -53,7 +53,7 @@
                 android:layout_marginTop="@dimen/notification_progress_margin_top"
                 android:layout_marginBottom="2dp"/>
             <TextView android:id="@+id/inbox_text0"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -62,7 +62,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text1"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -71,7 +71,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text2"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -80,7 +80,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text3"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -89,7 +89,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text4"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -98,7 +98,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text5"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -107,7 +107,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text6"
-                style="@style/Widget.Material.Notification.Text"
+                style="@style/Widget.DeviceDefault.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 0717d96..483b479 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -34,7 +34,7 @@
         android:orientation="vertical">
         <com.android.internal.widget.ImageFloatingTextView
             android:id="@+id/message_name"
-            style="@style/Widget.Material.Notification.MessagingName"
+            style="@style/Widget.DeviceDefault.Notification.MessagingName"
             android:layout_width="wrap_content"
             android:textAlignment="viewStart"
         />
diff --git a/core/res/res/layout/notification_template_messaging_text_message.xml b/core/res/res/layout/notification_template_messaging_text_message.xml
index 3611186..26c081e 100644
--- a/core/res/res/layout/notification_template_messaging_text_message.xml
+++ b/core/res/res/layout/notification_template_messaging_text_message.xml
@@ -18,5 +18,5 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/message_text"
     android:textAlignment="viewStart"
-    style="@style/Widget.Material.Notification.MessagingText"
+    style="@style/Widget.DeviceDefault.Notification.MessagingText"
 />
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index 6459bb8..622f080 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -22,7 +22,7 @@
     android:orientation="horizontal"
     >
     <TextView android:id="@+id/title"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Title"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:singleLine="true"
@@ -31,7 +31,7 @@
         android:textAlignment="viewStart"
         />
     <TextView android:id="@+id/text_line_1"
-        style="@style/Widget.Material.Notification.Text"
+        style="@style/Widget.DeviceDefault.Notification.Text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="end|bottom"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 3b27666..01b14ae 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License
   -->
 <com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/Widget.Material.Notification.Text"
+    style="@style/Widget.DeviceDefault.Notification.Text"
     android:id="@+id/text"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f55e48e..8dbea38 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6034,7 +6034,7 @@
         <attr name="insetBottom" format="fraction|dimension" />
     </declare-styleable>
 
-    <!-- Drawable used to draw animated images (gif) -->
+    <!-- Drawable used to draw animated images (gif). -->
     <declare-styleable name="AnimatedImageDrawable">
         <!-- Identifier of the image file. This attribute is mandatory.
              It must be an image file with multiple frames, e.g. gif or webp -->
@@ -8918,8 +8918,8 @@
         <attr name="name" />
     </declare-styleable>
 
-    <!-- Attributes that are read when parsing a <font> tag, which is a child of
-         <font-family>. This represents an actual font file and its attributes. -->
+    <!-- Attributes that are read when parsing a &lt;font&gt; tag, which is a child of
+         &lt;font-family&gt;. This represents an actual font file and its attributes. -->
     <declare-styleable name="FontFamilyFont">
         <!-- The style of the given font file. This will be used when the font is being loaded into
          the font stack and will override any style information in the font's header tables. If
@@ -8950,7 +8950,7 @@
         <attr name="fontVariationSettings" format="string" />
     </declare-styleable>
 
-    <!-- Attributes that are read when parsing a <fontfamily> tag. -->
+    <!-- Attributes that are read when parsing a &lt;fontfamily&gt; tag. -->
     <declare-styleable name="FontFamily">
         <!-- The authority of the Font Provider to be used for the request. -->
         <attr name="fontProviderAuthority" format="string" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0cd6bc5..499278c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3350,6 +3350,14 @@
     -->
     <string name="config_defaultTextClassifierPackage" translatable="false"></string>
 
+    <!-- The package name for the system's smart suggestion service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         If no service with the specified name exists on the device, content capture and
+         smart suggestions will be disabled.
+         Example: "com.android.intelligence/.SmartSuggestionsService"
+    -->
+    <string name="config_defaultSmartSuggestionsService" translatable="false"></string>
+
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">true</bool>
 
@@ -3487,7 +3495,13 @@
     <!-- Controls whether system buttons use all caps for text -->
     <bool name="config_buttonTextAllCaps">true</bool>
     <!-- Name of the font family used for system surfaces where the font should use medium weight -->
-    <string name="config_headlineFontFamilyMedium">@string/font_family_button_material</string>
+    <string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
+    <!-- Name of a font family to use for body text. -->
+    <string name="config_bodyFontFamily" translatable="false">sans-serif</string>
+    <!-- Name of a font family to use for light body text. -->
+    <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
+    <!-- Name of a font family to use for medium body text. -->
+    <string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
 
     <!-- Size of icon shown beside a preference locked by admin -->
     <dimen name="config_restrictedIconSize">@dimen/restricted_icon_size_material</dimen>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index d722961..2c04ec8 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -113,6 +113,15 @@
     <style name="Widget.DeviceDefault.ListView.White" parent="Widget.Material.ListView.White"/>
     <style name="Widget.DeviceDefault.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
     <style name="Widget.DeviceDefault.NumberPicker" parent="Widget.Material.NumberPicker"/>
+    <style name="Widget.DeviceDefault.Notification.Text" parent="Widget.Material.Notification.Text">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
+    </style>
+    <style name="Widget.DeviceDefault.Notification.MessagingText" parent="Widget.Material.Notification.MessagingText">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
+    </style>
+    <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
+    </style>
     <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
     <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
     <style name="Widget.DeviceDefault.ProgressBar.Large.Inverse" parent="Widget.Material.ProgressBar.Large.Inverse"/>
@@ -218,41 +227,100 @@
     <style name="Widget.DeviceDefault.Light.TimePicker" parent="Widget.Material.Light.TimePicker"/>
 
     <!-- Text Appearance Styles -->
-    <style name="TextAppearance.DeviceDefault" parent="TextAppearance.Material"/>
-    <style name="TextAppearance.DeviceDefault.Inverse" parent="TextAppearance.Material.Inverse"/>
-    <style name="TextAppearance.DeviceDefault.Large" parent="TextAppearance.Material.Large"/>
-    <style name="TextAppearance.DeviceDefault.Large.Inverse" parent="TextAppearance.Material.Large.Inverse"/>
-    <style name="TextAppearance.DeviceDefault.Medium" parent="TextAppearance.Material.Medium"/>
-    <style name="TextAppearance.DeviceDefault.Medium.Inverse" parent="TextAppearance.Material.Medium.Inverse"/>
-    <style name="TextAppearance.DeviceDefault.Small" parent="TextAppearance.Material.Small"/>
-    <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Material.Small.Inverse"/>
-    <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title"/>
-    <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle"/>
-    <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget"/>
+    <style name="TextAppearance.DeviceDefault" parent="TextAppearance.Material">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Inverse" parent="TextAppearance.Material.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Large" parent="TextAppearance.Material.Large">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Large.Inverse" parent="TextAppearance.Material.Large.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Medium" parent="TextAppearance.Material.Medium">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Medium.Inverse" parent="TextAppearance.Material.Medium.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Small" parent="TextAppearance.Material.Small">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Material.Small.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification">
+        <item name="fontFamily">@string/config_headlineFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
+        <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Material.Widget.Button">
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
         <item name="textAllCaps">@bool/config_buttonTextAllCaps</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item"/>
-    <style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Material.Widget.TabWidget"/>
-    <style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Material.Widget.TextView"/>
-    <style name="TextAppearance.DeviceDefault.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Widget.TextView.PopupMenu"/>
-    <style name="TextAppearance.DeviceDefault.Widget.DropDownHint" parent="TextAppearance.Material.Widget.DropDownHint"/>
-    <style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Material.Widget.DropDownItem"/>
-    <style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Widget.TextView.SpinnerItem"/>
-    <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText"/>
+    <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Material.Widget.TabWidget">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Material.Widget.TextView">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Widget.TextView.PopupMenu">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.DropDownHint" parent="TextAppearance.Material.Widget.DropDownHint">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Material.Widget.DropDownItem">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Widget.TextView.SpinnerItem">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button">
         <item name="textColor">@color/btn_colored_borderless_text_material</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu"/>
-    <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large"/>
-    <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small"/>
+    <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title">
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle"/>
-    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title"/>
-    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle"/>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Material.WindowTitle">
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
     </style>
@@ -260,15 +328,25 @@
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
     </style>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
-    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Title.Inverse"/>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Title.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
-    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse"/>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
-    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Title.Inverse"/>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Title.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
-    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
-    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
-    <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title" />
+    <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse">
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu">
+        <item name="fontFamily">@string/config_bodyFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title"/>
 
     <!-- Preference Styles -->
     <style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 67b3c92..5a7199d 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1313,4 +1313,5 @@
         <item name="gravity">top|center_horizontal</item>
     </style>
 
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01422c8..6854a84e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3265,6 +3265,7 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
+  <java-symbol type="string" name="config_defaultSmartSuggestionsService" />
 
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fa009bd..fec101a 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1724,4 +1724,10 @@
         <item name="layout_gravity">center</item>
     </style>
 
+    <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
+    </style>
+
+    <style name="Theme.DeviceDefault.Notification.Ambient" parent="@style/Theme.Material.Notification.Ambient">
+    </style>
+
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a8f9e8a..ed9c3d5 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -415,6 +415,7 @@
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
                     Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
                     Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
+                    Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED,
                     Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED,
                     Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
                     Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
@@ -477,6 +478,7 @@
                     Settings.Global.USER_SWITCHER_ENABLED,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
+                    Settings.Global.USB_ALARM_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
                     Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
                     Settings.Global.WEBVIEW_MULTIPROCESS,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index fbcb629..aec4571 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -373,7 +373,7 @@
     public void testSuggestConversationActions_textReplyOnly_maxThree() {
         if (isTextClassifierDisabled()) return;
         ConversationActions.Message message =
-                new ConversationActions.Message.Builder().setText("Hello").build();
+                new ConversationActions.Message.Builder().setText("Where are you?").build();
         ConversationActions.TypeConfig typeConfig =
                 new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
                         .setIncludedTypes(
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 8691e73..e261819 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.os;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
@@ -387,8 +388,7 @@
 
     @Test
     public void testNoDataCollectedBeforeInitialDeviceStateSet() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setDeviceState(null);
+        TestBinderCallsStats bcs = new TestBinderCallsStats(null);
         bcs.setDetailedTracking(true);
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
@@ -528,22 +528,6 @@
     }
 
     @Test
-    public void testCallingUidUsedWhenWorkSourceNotSet() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setDetailedTracking(true);
-        bcs.workSourceUid = -1;
-
-        Binder binder = new Binder();
-        CallSession callSession = bcs.callStarted(binder, 1);
-        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
-        assertEquals(1, bcs.getExportedCallStats().size());
-        BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
-        assertEquals(CALLING_UID, stat.workSourceUid);
-        assertEquals(CALLING_UID, stat.callingUid);
-    }
-
-    @Test
     public void testGetExportedStatsWithoutCalls() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         Binder binder = new Binder();
@@ -613,6 +597,27 @@
         assertEquals(CALLING_UID, callStats.callingUid);
     }
 
+    @Test
+    public void testAddsDebugEntries() {
+        long startTime = System.currentTimeMillis();
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setAddDebugEntries(true);
+        ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
+        assertEquals(3, callStats.size());
+        BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0);
+        assertEquals("", debugEntry1.className);
+        assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName);
+        assertTrue(startTime <= debugEntry1.latencyMicros);
+        BinderCallsStats.ExportedCallStat debugEntry2 = callStats.get(1);
+        assertEquals("", debugEntry2.className);
+        assertEquals("__DEBUG_end_time_millis", debugEntry2.methodName);
+        assertTrue(debugEntry1.latencyMicros <= debugEntry2.latencyMicros);
+        BinderCallsStats.ExportedCallStat debugEntry3 = callStats.get(2);
+        assertEquals("", debugEntry3.className);
+        assertEquals("__DEBUG_battery_time_millis", debugEntry3.methodName);
+        assertTrue(debugEntry3.latencyMicros >= 0);
+    }
+
     class TestBinderCallsStats extends BinderCallsStats {
         public int callingUid = CALLING_UID;
         public int workSourceUid = WORKSOURCE_UID;
@@ -620,6 +625,10 @@
         public long elapsedTime = 0;
 
         TestBinderCallsStats() {
+            this(mDeviceState);
+        }
+
+        TestBinderCallsStats(CachedDeviceState deviceState) {
             // Make random generator not random.
             super(new Injector() {
                 public Random getRandomGenerator() {
@@ -633,7 +642,10 @@
                 }
             });
             setSamplingInterval(1);
-            setDeviceState(mDeviceState.getReadonlyClient());
+            setAddDebugEntries(false);
+            if (deviceState != null) {
+                setDeviceState(deviceState.getReadonlyClient());
+            }
         }
 
         @Override
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 31dde5c..f26dfad 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -273,8 +273,7 @@
 
     @Test
     public void testDataNotCollectedBeforeDeviceStateSet() {
-        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
-        looperStats.setDeviceState(null);
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100, null);
 
         Object token1 = looperStats.messageDispatchStarting();
         looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
@@ -439,15 +438,19 @@
         looperStats.messageDispatched(token, message);
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
-        assertThat(entries).hasSize(3);
+        assertThat(entries).hasSize(4);
         LooperStats.ExportedEntry debugEntry1 = entries.get(1);
         assertThat(debugEntry1.handlerClassName).isEqualTo("");
         assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
-        assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis());
+        assertThat(debugEntry1.totalLatencyMicros).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());
+        assertThat(debugEntry2.totalLatencyMicros).isAtLeast(looperStats.getStartTimeMillis());
+        LooperStats.ExportedEntry debugEntry3 = entries.get(3);
+        assertThat(debugEntry3.handlerClassName).isEqualTo("");
+        assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
+        assertThat(debugEntry3.totalLatencyMicros).isAtLeast(0L);
     }
 
     private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
@@ -468,10 +471,16 @@
         private int mSamplingInterval;
 
         TestableLooperStats(int samplingInterval, int sizeCap) {
+            this(samplingInterval, sizeCap, mDeviceState);
+        }
+
+        TestableLooperStats(int samplingInterval, int sizeCap, CachedDeviceState deviceState) {
             super(samplingInterval, sizeCap);
-            this.mSamplingInterval = samplingInterval;
-            this.setDeviceState(mDeviceState.getReadonlyClient());
-            this.setAddDebugEntries(false);
+            mSamplingInterval = samplingInterval;
+            setAddDebugEntries(false);
+            if (deviceState != null) {
+                setDeviceState(deviceState.getReadonlyClient());
+            }
         }
 
         void tickRealtime(long micros) {
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9e4ea32..f237344 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -281,6 +281,7 @@
         <permission name="android.permission.WRITE_APN_SETTINGS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.settings.intelligence">
@@ -408,6 +409,7 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index dea2f45..cb12a7c 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -150,11 +151,14 @@
      */
     AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
         mLayerState = createConstantState(state, res);
-
-        if (sMask == null) {
-            sMask = PathParser.createPathFromPathData(
-                Resources.getSystem().getString(R.string.config_icon_mask));
-        }
+        // config_icon_mask from context bound resource may have been chaged using
+        // OverlayManager. Read that one first.
+        Resources r = ActivityThread.currentActivityThread() == null
+                ? Resources.getSystem()
+                : ActivityThread.currentActivityThread().getApplication().getResources();
+        // TODO: either make sMask update only when config_icon_mask changes OR
+        // get rid of it all-together in layoutlib
+        sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
         mMask = new Path(sMask);
         mMaskScaleOnly = new Path(mMask);
         mMaskMatrix = new Matrix();
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 68d216d..c20c720 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -583,7 +583,65 @@
           loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
                                                             dtohl(entry_iter->packageId));
         }
+      } break;
 
+      case RES_TABLE_OVERLAYABLE_TYPE: {
+        const ResTable_overlayable_header* header =
+            child_chunk.header<ResTable_overlayable_header>();
+        if (header == nullptr) {
+          LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
+          return {};
+        }
+
+        // Iterate over the overlayable policy chunks
+        ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
+        while (overlayable_iter.HasNext()) {
+          const Chunk overlayable_child_chunk = overlayable_iter.Next();
+
+          switch (overlayable_child_chunk.type()) {
+            case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
+              const ResTable_overlayable_policy_header* policy_header =
+                  overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
+              if (policy_header == nullptr) {
+                LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
+                return {};
+              }
+
+              if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
+                  < dtohl(policy_header->entry_count)) {
+                LOG(ERROR) <<  "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
+                return {};
+              }
+
+              // Retrieve all the ids belonging to this policy
+              std::unordered_set<uint32_t> ids;
+              const auto ids_begin =
+                  reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+              const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
+              for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+                ids.insert(dtohl(id_iter->ident));
+              }
+
+              // Add the pairing of overlayable properties to resource ids to the package
+              OverlayableInfo overlayable_info;
+              overlayable_info.policy_flags = policy_header->policy_flags;
+              loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+              break;
+            }
+
+            default:
+              LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+              break;
+          }
+        }
+
+        if (overlayable_iter.HadError()) {
+          LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
+                                     overlayable_iter.GetLastError().c_str());
+          if (overlayable_iter.HadFatalError()) {
+            return {};
+          }
+        }
       } break;
 
       default:
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 2fe98b0..63b2527 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7076,7 +7076,7 @@
         }
     }
 
-    const auto& getTypeMapping() const {
+    const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
         return mTypeMapping->mData;
     }
 
@@ -7137,9 +7137,6 @@
 
     const PackageGroup* packageGroup = mPackageGroups[0];
 
-    // the number of resources overlaid that were not explicitly marked overlayable
-    size_t forcedOverlayCount = 0u;
-
     // find the resources that exist in both packages
     auto typeMapping = std::make_unique<IdmapTypeMapping>();
     for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
@@ -7170,11 +7167,6 @@
                 continue;
             }
 
-            if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
-                    ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
-                ++forcedOverlayCount;
-            }
-
             typeMapping->add(target_resid, overlay_resid);
         }
     }
@@ -7243,10 +7235,6 @@
         typeData += entryCount * 2;
     }
 
-    if (forcedOverlayCount > 0) {
-        ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount);
-    }
-
     return NO_ERROR;
 }
 
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 99a52dc..a0f2343 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -89,7 +89,9 @@
         len_(len),
         last_error_(nullptr) {
     CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
-    VerifyNextChunk();
+    if (len_ != 0) {
+      VerifyNextChunk();
+    }
   }
 
   Chunk Next();
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 349b379..8c5c3b7 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <set>
 #include <vector>
+#include <unordered_set>
 
 #include "android-base/macros.h"
 
@@ -76,6 +77,10 @@
 // TypeSpecPtr is a managed pointer that knows how to delete itself.
 using TypeSpecPtr = util::unique_cptr<TypeSpec>;
 
+struct OverlayableInfo {
+  uint32_t policy_flags;
+};
+
 class LoadedPackage {
  public:
   class iterator {
@@ -216,6 +221,18 @@
     }
   }
 
+  // Retrieve the overlayable properties of the specified resource. If the resource is not
+  // overlayable, this will return a null pointer.
+  const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const {
+    for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids
+        : overlayable_infos_) {
+      if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) {
+        return &overlayable_info_ids.first;
+      }
+    }
+    return nullptr;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
 
@@ -233,6 +250,7 @@
   ByteBucketArray<TypeSpecPtr> type_specs_;
   ByteBucketArray<uint32_t> resource_ids_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
+  std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
 };
 
 // Read-only view into a resource table. This class validates all data
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index ad33fcf..91261aa 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -234,7 +234,9 @@
     RES_TABLE_PACKAGE_TYPE      = 0x0200,
     RES_TABLE_TYPE_TYPE         = 0x0201,
     RES_TABLE_TYPE_SPEC_TYPE    = 0x0202,
-    RES_TABLE_LIBRARY_TYPE      = 0x0203
+    RES_TABLE_LIBRARY_TYPE      = 0x0203,
+    RES_TABLE_OVERLAYABLE_TYPE  = 0x0204,
+    RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
 };
 
 /**
@@ -1354,10 +1356,6 @@
     enum : uint32_t {
         // Additional flag indicating an entry is public.
         SPEC_PUBLIC = 0x40000000u,
-
-        // Additional flag indicating an entry is overlayable at runtime.
-        // Added in Android-P.
-        SPEC_OVERLAYABLE = 0x80000000u,
     };
 };
 
@@ -1607,6 +1605,49 @@
     uint16_t packageName[128];
 };
 
+/**
+ * Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
+ */
+struct ResTable_overlayable_header
+{
+  struct ResChunk_header header;
+};
+
+/**
+ * Holds a list of resource ids that are protected from being overlaid by a set of policies. If
+ * the overlay fulfils at least one of the policies, then the overlay can overlay the list of
+ * resources.
+ */
+struct ResTable_overlayable_policy_header
+{
+  struct ResChunk_header header;
+
+  enum PolicyFlags {
+    // Any overlay can overlay these resources.
+    POLICY_PUBLIC = 0x00000001,
+
+    // The overlay must reside of the system partition or must have existed on the system partition
+    // before an upgrade to overlay these resources.
+    POLICY_SYSTEM_PARTITION = 0x00000002,
+
+    // The overlay must reside of the vendor partition or must have existed on the vendor partition
+    // before an upgrade to overlay these resources.
+    POLICY_VENDOR_PARTITION = 0x00000004,
+
+    // The overlay must reside of the product partition or must have existed on the product
+    // partition before an upgrade to overlay these resources.
+    POLICY_PRODUCT_PARTITION = 0x00000008,
+
+    // The overlay must reside of the product services partition or must have existed on the product
+    // services partition before an upgrade to overlay these resources.
+    POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010,
+  };
+  uint32_t policy_flags;
+
+  // The number of ResTable_ref that follow this header.
+  uint32_t entry_count;
+};
+
 struct alignas(uint32_t) Idmap_header {
   // Always 0x504D4449 ('IDMP')
   uint32_t magic;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index ffa4836..441356b 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -22,12 +22,14 @@
 #include "TestHelpers.h"
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
+#include "data/overlayable/R.h"
 #include "data/sparse/R.h"
 #include "data/styles/R.h"
 
 namespace app = com::android::app;
 namespace basic = com::android::basic;
 namespace libclient = com::android::libclient;
+namespace overlayable = com::android::overlayable;
 namespace sparse = com::android::sparse;
 
 using ::android::base::ReadFileToString;
@@ -273,10 +275,44 @@
   ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
 }
 
-// structs with size fields (like Res_value, ResTable_entry) should be
-// backwards and forwards compatible (aka checking the size field against
-// sizeof(Res_value) might not be backwards compatible.
-TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+TEST(LoadedArscTest, LoadOverlayable) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
+                                      "resources.arsc", &contents));
+
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+                       false /*load_as_shared_library*/);
+
+  ASSERT_THAT(loaded_arsc, NotNull());
+  const LoadedPackage* package = loaded_arsc->GetPackageById(
+      get_package_id(overlayable::R::string::not_overlayable));
+
+  const OverlayableInfo* info = package->GetOverlayableInfo(
+      overlayable::R::string::not_overlayable);
+  ASSERT_THAT(info, IsNull());
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags,
+              Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
+                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags,
+              Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
+                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
+                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+}
 
 TEST(LoadedArscTest, ResourceIdentifierIterator) {
   std::string contents;
@@ -326,4 +362,9 @@
   ASSERT_EQ(end, iter);
 }
 
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
 }  // namespace android
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index 33f9611..d37874d 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
similarity index 64%
copy from packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
copy to libs/androidfw/tests/data/overlayable/AndroidManifest.xml
index 5901ff4..abc2a45 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
+++ b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<?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.
@@ -12,12 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <objectAnimator
-        android:duration="83"
-        android:propertyName="alpha"
-        android:valueType="floatType"
-        android:valueFrom="0"
-        android:valueTo="1" />
-</set>
\ No newline at end of file
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.overlayable">
+    <application>
+    </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
new file mode 100644
index 0000000..e46e264d
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef TESTS_DATA_OVERLAYABLE_R_H_
+#define TESTS_DATA_OVERLAYABLE_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlayable {
+
+struct R {
+  struct string {
+    enum : uint32_t {
+      not_overlayable = 0x7f010000,
+      overlayable1 = 0x7f010001,
+      overlayable2 = 0x7f010002,
+      overlayable3 = 0x7f010003,
+      overlayable4 = 0x7f010004,
+    };
+  };
+};
+
+}  // namespace overlayable
+}  // namespace android
+}  // namespace com
+
+#endif /* TESTS_DATA_OVERLAYABLE_R_H_ */
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
new file mode 100755
index 0000000..98fdc51
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+#
+
+set -e
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
new file mode 100644
index 0000000..85ab4be
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
new file mode 100644
index 0000000..11aa735
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -0,0 +1,41 @@
+<?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>
+<overlayable>
+    <!-- Any overlay can overlay the value of @string/overlayable1 -->
+    <item type="string" name="overlayable1" />
+
+    <!-- Any overlay on the product or system partition can overlay the value of
+        @string/overlayable2 -->
+    <policy type="product|system">
+        <item type="string" name="overlayable2" />
+    </policy>
+
+    <!-- Any overlay can overlay the value of @string/overlayable4 -->
+    <policy type="public">
+        <item type="string" name="overlayable4" />
+    </policy>
+</overlayable>
+
+<overlayable>
+    <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
+   @string/overlayable3 -->
+    <policy type="product_services|vendor|product">
+        <item type="string" name="overlayable3" />
+    </policy>
+</overlayable>
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
new file mode 100644
index 0000000..5676d7c
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.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.
+-->
+
+<resources>
+    <public type="string" name="not_overlayable" id="0x7f010000" />
+    <public type="string" name="overlayable1" id="0x7f010001" />
+    <public type="string" name="overlayable2" id="0x7f010002" />
+    <public type="string" name="overlayable3" id="0x7f010003" />
+    <public type="string" name="overlayable4" id="0x7f010004" />
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
new file mode 100644
index 0000000..a86b312
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.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.
+-->
+
+<resources>
+    <string name="not_overlayable">Not overlayable</string>
+    <string name="overlayable1">Overlayable One</string>
+    <string name="overlayable2">Overlayable Two</string>
+    <string name="overlayable3">Overlayable Three</string>
+    <string name="overlayable4">Overlayable Four</string>
+</resources>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index da77b99..ed7d5e5 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -171,6 +171,7 @@
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/SkiaVulkanPipeline.cpp",
         "pipeline/skia/VectorDrawableAtlas.cpp",
+        "pipeline/skia/VkFunctorDrawable.cpp",
         "pipeline/skia/VkInteropFunctorDrawable.cpp",
         "renderstate/RenderState.cpp",
         "renderthread/CacheManager.cpp",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 4a3e10c..8067313 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -29,8 +29,6 @@
 namespace android {
 namespace uirenderer {
 
-bool Properties::drawDeferDisabled = false;
-bool Properties::drawReorderDisabled = false;
 bool Properties::debugLayersUpdates = false;
 bool Properties::debugOverdraw = false;
 bool Properties::showDirtyRegions = false;
@@ -40,7 +38,6 @@
 
 DebugLevel Properties::debugLevel = kDebugDisabled;
 OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
-StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide;
 
 float Properties::overrideLightRadius = -1.0f;
 float Properties::overrideLightPosY = -1.0f;
@@ -85,7 +82,6 @@
     char property[PROPERTY_VALUE_MAX];
     bool prevDebugLayersUpdates = debugLayersUpdates;
     bool prevDebugOverdraw = debugOverdraw;
-    StencilClipDebug prevDebugStencilClip = debugStencilClip;
 
     debugOverdraw = false;
     if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) {
@@ -99,20 +95,6 @@
         }
     }
 
-    // See Properties.h for valid values
-    if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) {
-        INIT_LOGD("  Stencil clip debug enabled: %s", property);
-        if (!strcmp(property, "hide")) {
-            debugStencilClip = StencilClipDebug::Hide;
-        } else if (!strcmp(property, "highlight")) {
-            debugStencilClip = StencilClipDebug::ShowHighlight;
-        } else if (!strcmp(property, "region")) {
-            debugStencilClip = StencilClipDebug::ShowRegion;
-        }
-    } else {
-        debugStencilClip = StencilClipDebug::Hide;
-    }
-
     sProfileType = ProfileType::None;
     if (property_get(PROPERTY_PROFILE, property, "") > 0) {
         if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) {
@@ -125,12 +107,6 @@
     debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false);
     INIT_LOGD("  Layers updates debug enabled: %d", debugLayersUpdates);
 
-    drawDeferDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_DEFER, false);
-    INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
-
-    drawReorderDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_REORDER, false);
-    INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
-
     showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
 
     debugLevel = (DebugLevel)property_get_int(PROPERTY_DEBUG, kDebugDisabled);
@@ -152,8 +128,7 @@
 
     enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true);
 
-    return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
-           (prevDebugStencilClip != debugStencilClip);
+    return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
 
 void Properties::overrideProperty(const char* name, const char* value) {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index da53f66..0a7f4e7 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -95,20 +95,6 @@
 #define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"
 
 /**
- * Used to enable/disable non-rectangular clipping debugging.
- *
- * The accepted values are:
- * "highlight", drawing commands clipped by the stencil will
- *              be colored differently
- * "region", renders the clipping region on screen whenever
- *           the stencil is set
- * "hide", don't show the clip
- *
- * The default value is "hide".
- */
-#define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip"
-
-/**
  * Turn on to draw dirty regions every other frame.
  *
  * Possible values:
@@ -118,19 +104,6 @@
 #define PROPERTY_DEBUG_SHOW_DIRTY_REGIONS "debug.hwui.show_dirty_regions"
 
 /**
- * Disables draw operation deferral if set to "true", forcing draw
- * commands to be issued to OpenGL in order, and processed in sequence
- * with state-manipulation canvas commands.
- */
-#define PROPERTY_DISABLE_DRAW_DEFER "debug.hwui.disable_draw_defer"
-
-/**
- * Used to disable draw operation reordering when deferring draw operations
- * Has no effect if PROPERTY_DISABLE_DRAW_DEFER is set to "true"
- */
-#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
-
-/**
  * Setting this property will enable or disable the dropping of frames with
  * empty damage. Default is "true".
  */
@@ -207,8 +180,6 @@
 
 enum class OverdrawColorSet { Default = 0, Deuteranomaly };
 
-enum class StencilClipDebug { Hide, ShowHighlight, ShowRegion };
-
 enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
 
 /**
@@ -220,8 +191,6 @@
 public:
     static bool load();
 
-    static bool drawDeferDisabled;
-    static bool drawReorderDisabled;
     static bool debugLayersUpdates;
     static bool debugOverdraw;
     static bool showDirtyRegions;
@@ -235,7 +204,6 @@
 
     static DebugLevel debugLevel;
     static OverdrawColorSet overdrawColorSet;
-    static StencilClipDebug debugStencilClip;
 
     // Override the value for a subset of properties in this class
     static void overrideProperty(const char* name, const char* value);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 4a63910..00ce28a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -454,6 +454,9 @@
 using StringBuffer = FatVector<char, 128>;
 
 template <typename... T>
+// TODO:__printflike(2, 3)
+// Doesn't work because the warning doesn't understand string_view and doesn't like that
+// it's not a C-style variadic function.
 static void format(StringBuffer& buffer, const std::string_view& format, T... args) {
     buffer.resize(buffer.capacity());
     while (1) {
@@ -468,19 +471,20 @@
             buffer.resize(needed + 1);
             return;
         }
-        buffer.resize(buffer.size() * 2);
+        // If we're doing a heap alloc anyway might as well give it some slop
+        buffer.resize(needed + 100);
     }
 }
 
 void RenderNode::markDrawStart(SkCanvas& canvas) {
     StringBuffer buffer;
-    format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName());
+    format(buffer, "RenderNode(id=%" PRId64 ", name='%s')", uniqueId(), getName());
     canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
 }
 
 void RenderNode::markDrawEnd(SkCanvas& canvas) {
     StringBuffer buffer;
-    format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName());
+    format(buffer, "/RenderNode(id=%" PRId64 ", name='%s')", uniqueId(), getName());
     canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
 }
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index ba34384..6be7ef7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -681,7 +681,7 @@
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
-    SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+    SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
     // older.
     if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 &&
@@ -708,7 +708,7 @@
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
-    SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+    SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
 
     const int N = end - start;
     SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 769fce4..84292c8 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -43,7 +43,7 @@
 static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
                                          const minikin::MinikinPaint& paint,
                                          const minikin::FontFakery& fakery) {
-    skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    skPaint->setTextEncoding(kGlyphID_SkTextEncoding);
     skPaint->setTextSize(paint.size);
     skPaint->setTextScaleX(paint.scaleX);
     skPaint->setTextSkewX(paint.skewX);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b682ab0..b56c3ef 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -25,6 +25,7 @@
 #include "pipeline/skia/AnimatedDrawables.h"
 #include "pipeline/skia/GLFunctorDrawable.h"
 #include "pipeline/skia/VkInteropFunctorDrawable.h"
+#include "pipeline/skia/VkFunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -124,6 +125,8 @@
                                              uirenderer::GlFunctorLifecycleListener* listener) {
     FunctorDrawable* functorDrawable;
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
+        // interop is disabled/moved.
         functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
                 listener, asSkCanvas());
     } else {
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 0000000..71ad5e1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#include "VkFunctorDrawable.h"
+#include <private/hwui/DrawVkInfo.h>
+
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <GrBackendDrawableInfo.h>
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+#include <SkImage.h>
+#include <vk/GrVkTypes.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
+    : INHERITED()
+    , mFunctor(functor) {}
+
+VkFunctorDrawHandler::~VkFunctorDrawHandler() {
+    // TODO(cblume) Fill in the DrawVkInfo parameters.
+    (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr);
+}
+
+void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
+    ATRACE_CALL();
+
+    GrVkDrawableInfo vulkan_info;
+    if (!info.getVkDrawableInfo(&vulkan_info)) {
+        return;
+    }
+
+    DrawVkInfo draw_vk_info;
+    // TODO(cblume) Fill in the rest of the parameters and test the actual call.
+    draw_vk_info.isLayer = true;
+
+    (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
+}
+
+VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
+                                     SkCanvas* canvas)
+    : FunctorDrawable(functor, listener, canvas) {}
+
+VkFunctorDrawable::~VkFunctorDrawable() = default;
+
+void VkFunctorDrawable::syncFunctor() const {
+    (*mFunctor)(DrawVkInfo::kModeSync, nullptr);
+}
+
+void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
+    LOG_ALWAYS_FATAL("VkFunctorDrawable::onDraw() should never be called.");
+    // Instead of calling onDraw(), the call should come from onSnapGpuDrawHandler.
+}
+
+std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
+    GrBackendApi backendApi, const SkMatrix& matrix) {
+    if (backendApi != GrBackendApi::kVulkan) {
+        return nullptr;
+    }
+    std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
+    return std::move(draw);
+}
+
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
new file mode 100644
index 0000000..5cd1314
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+#include <SkImageInfo.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * This draw handler will be returned by VkFunctorDrawable's onSnapGpuDrawHandler. It allows us to
+ * issue Vulkan commands while the command buffer is being flushed.
+ */
+class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler {
+public:
+    explicit VkFunctorDrawHandler(Functor* functor);
+    ~VkFunctorDrawHandler() override;
+
+    void draw(const GrBackendDrawableInfo& info) override;
+private:
+    typedef GpuDrawHandler INHERITED;
+
+    Functor* mFunctor;
+};
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list of Skia drawing
+ * commands.
+ */
+class VkFunctorDrawable : public FunctorDrawable {
+public:
+    VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
+            SkCanvas* canvas);
+    ~VkFunctorDrawable() override;
+
+    void syncFunctor() const override;
+
+protected:
+    // SkDrawable functions:
+    void onDraw(SkCanvas* canvas) override;
+    std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
+            const SkMatrix& matrix) override;
+};
+
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 004a558..8228550 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -46,7 +46,7 @@
     ScopedDrawRequest() { beginDraw(); }
 private:
     void beginDraw() {
-        std::lock_guard{sLock};
+        std::lock_guard _lock{sLock};
 
         if (!sGLDrawThread) {
             sGLDrawThread = new ThreadBase{};
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
new file mode 100644
index 0000000..019950f
--- /dev/null
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_DRAW_VK_INFO_H
+#define ANDROID_HWUI_DRAW_VK_INFO_H
+
+#include <vulkan/vulkan.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan
+ * functors.
+ */
+struct DrawVkInfo {
+    // Input: current width/height of destination surface
+    int width;
+    int height;
+
+    // Input: is the render target an FBO
+    bool isLayer;
+
+    // Input: current transform matrix, in OpenGL format
+    float transform[16];
+
+    // Input: WebView should do its main compositing draws into this. It cannot do anything that
+    // would require stopping the render pass.
+    VkCommandBuffer secondaryCommandBuffer;
+
+    // Input: The main color attachment index where secondaryCommandBuffer will eventually be
+    // submitted.
+    uint32_t colorAttachmentIndex;
+
+    // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer
+    // will be submitted into.
+    VkRenderPass compatibleRenderPass;
+
+    // Input: Format of the destination surface.
+    VkFormat format;
+
+    // Input: Color space transfer params
+    float G;
+    float A;
+    float B;
+    float C;
+    float D;
+    float E;
+    float F;
+
+    // Input: Color space transformation from linear RGB to D50-adapted XYZ
+    float matrix[9];
+
+    // Input: current clip rect
+    int clipLeft;
+    int clipTop;
+    int clipRight;
+    int clipBottom;
+
+    /**
+     * Values used as the "what" parameter of the functor.
+     */
+    enum Mode {
+        // Called once at WebView start
+        kModeInit,
+        // Called when things need to be re-created
+        kModeReInit,
+        // Notifies the app that the composite functor will be called soon. This allows WebView to
+        // begin work early.
+        kModePreComposite,
+        // Do the actual composite work
+        kModeComposite,
+        // This allows WebView to begin using the previously submitted objects in future work.
+        kModePostComposite,
+        // Invoked every time the UI thread pushes over a frame to the render thread and the owning
+        // view has a dirty display list*. This is a signal to sync any data that needs to be
+        // shared between the UI thread and the render thread. During this time the UI thread is
+        // blocked.
+        kModeSync
+    };
+
+    /**
+     * Values used by Vulkan functors to tell the framework what to do next.
+     */
+    enum Status {
+        // The functor is done
+        kStatusDone = 0x0,
+    };
+};  // struct DrawVkInfo
+
+}  // namespace uirenderer
+}  // namespace android
+
+#endif  // ANDROID_HWUI_DRAW_VK_INFO_H
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e1f8307..2abb3d5 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -745,7 +745,7 @@
     surface->mWindowWidth = extent.width;
     surface->mWindowHeight = extent.height;
 
-    uint32_t imageCount = caps.minImageCount + 2;
+    uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount);
     if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
         // Application must settle for fewer images than desired:
         imageCount = caps.maxImageCount;
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 8a1bc4d..f812022 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -81,7 +81,7 @@
     auto utf16 = asciiToUtf16(text);
     uint32_t length = strlen(text);
     SkPaint glyphPaint(paint);
-    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawText(
             utf16.get(), length,  // text buffer
             0, length,  // draw range
@@ -94,7 +94,7 @@
                                  const SkPath& path) {
     auto utf16 = asciiToUtf16(text);
     SkPaint glyphPaint(paint);
-    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
             nullptr);
 }
diff --git a/libs/incident/proto/android/os/metadata.proto b/libs/incident/proto/android/os/metadata.proto
index f8f4e36..3b0e9c9 100644
--- a/libs/incident/proto/android/os/metadata.proto
+++ b/libs/incident/proto/android/os/metadata.proto
@@ -61,8 +61,10 @@
         optional bool timed_out = 7;
         // true if the section is truncated.
         optional bool is_truncated = 8;
+        // message for debugging if there is an error.
+        optional string error_msg = 9;
 
-        // Next Tag: 9
+        // Next Tag: 10;
     }
     repeated SectionStats sections = 6;
 
diff --git a/media/OWNERS b/media/OWNERS
index 0abf9ae..03b751c 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,12 +1,13 @@
+chz@google.com
+dwkang@google.com
 elaurent@google.com
 etalvala@google.com
 gkasten@google.com
 hunga@google.com
+jaewan@google.com
 jmtrivi@google.com
+jsharkey@android.com
 lajos@google.com
 marcone@google.com
 sungsoo@google.com
 wjia@google.com
-jaewan@google.com
-chz@google.com
-dwkang@google.com
diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java
index 0e8e6ce..cb9669b 100644
--- a/media/java/android/media/CallbackDataSourceDesc.java
+++ b/media/java/android/media/CallbackDataSourceDesc.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 
 /**
- * @hide
  * Structure of data source descriptor for sources using callback.
  *
  * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
diff --git a/media/java/android/media/CloseGuard.java b/media/java/android/media/CloseGuard.java
new file mode 100644
index 0000000..2014673
--- /dev/null
+++ b/media/java/android/media/CloseGuard.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.util.Log;
+
+/**
+ * Note: This file is copied from dalvik.system package with the following modifications:
+ *       - Remove @CorePlatformApi, @IntraCoreApi and @UnsupportedAppUsage annotations.
+ *       - Replace System.logW() with android.util.Log.w().
+ *       This file should be used only within media mainline module.
+ * TODO: Remove this file and use dalvik.system.CloseGuard once
+ *       @CorePlatformApi becomes stable or we have a replacement in SDK API.
+ *       b/120419300
+ *
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre>   {@code
+ *   class Foo {
+ *
+ *       {@literal @}ReachabilitySensitive
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Foo() {
+ *           ...;
+ *           guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up is
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre>   {@code
+ *   class Bar {
+ *
+ *       {@literal @}ReachabilitySensitive
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Bar() {
+ *           ...;
+ *       }
+ *
+ *       public void connect() {
+ *          ...;
+ *          guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * When used in a constructor, calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ *
+ * The @ReachabilitySensitive annotation ensures that finalize() cannot be
+ * called during the explicit call to cleanup(), prior to the guard.close call.
+ * There is an extremely small chance that, for code that neglects to call
+ * cleanup(), finalize() and thus cleanup() will be called while a method on
+ * the object is still active, but the "this" reference is no longer required.
+ * If missing cleanup() calls are expected, additional @ReachabilitySensitive
+ * annotations or reachabilityFence() calls may be required.
+ *
+ * @hide
+ */
+final class CloseGuard {
+
+    /**
+     * True if collection of call-site information (the expensive operation
+     * here)  and tracking via a Tracker (see below) are enabled.
+     * Enabled by default so we can diagnose issues early in VM startup.
+     * Note, however, that Android disables this early in its startup,
+     * but enables it with DropBoxing for system apps on debug builds.
+     */
+    private static volatile boolean stackAndTrackingEnabled = true;
+
+    /**
+     * Hook for customizing how CloseGuard issues are reported.
+     * Bypassed if stackAndTrackingEnabled was false when open was called.
+     */
+    private static volatile Reporter reporter = new DefaultReporter();
+
+    /**
+     * Hook for customizing how CloseGuard issues are tracked.
+     */
+    private static volatile Tracker currentTracker = null; // Disabled by default.
+
+    /**
+     * Returns a CloseGuard instance. {@code #open(String)} can be used to set
+     * up the instance to warn on failure to close.
+     */
+    public static CloseGuard get() {
+        return new CloseGuard();
+    }
+
+    /**
+     * Enables/disables stack capture and tracking. A call stack is captured
+     * during open(), and open/close events are reported to the Tracker, only
+     * if enabled is true. If a stack trace was captured, the {@link
+     * #getReporter() reporter} is informed of unclosed resources; otherwise a
+     * one-line warning is logged.
+     */
+    public static void setEnabled(boolean enabled) {
+        CloseGuard.stackAndTrackingEnabled = enabled;
+    }
+
+    /**
+     * True if CloseGuard stack capture and tracking are enabled.
+     */
+    public static boolean isEnabled() {
+        return stackAndTrackingEnabled;
+    }
+
+    /**
+     * Used to replace default Reporter used to warn of CloseGuard
+     * violations when stack tracking is enabled. Must be non-null.
+     */
+    public static void setReporter(Reporter rep) {
+        if (rep == null) {
+            throw new NullPointerException("reporter == null");
+        }
+        CloseGuard.reporter = rep;
+    }
+
+    /**
+     * Returns non-null CloseGuard.Reporter.
+     */
+    public static Reporter getReporter() {
+        return reporter;
+    }
+
+    /**
+     * Sets the {@link Tracker} that is notified when resources are allocated and released.
+     * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
+     * was called. A null argument disables tracking.
+     *
+     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+     * MUST NOT be used for any other purposes.
+     */
+    public static void setTracker(Tracker tracker) {
+        currentTracker = tracker;
+    }
+
+    /**
+     * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
+     * there is none.
+     *
+     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+     * MUST NOT be used for any other purposes.
+     */
+    public static Tracker getTracker() {
+        return currentTracker;
+    }
+
+    private CloseGuard() {}
+
+    /**
+     * {@code open} initializes the instance with a warning that the caller
+     * should have explicitly called the {@code closer} method instead of
+     * relying on finalization.
+     *
+     * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
+     * @throws NullPointerException if closer is null.
+     */
+    public void open(String closer) {
+        // always perform the check for valid API usage...
+        if (closer == null) {
+            throw new NullPointerException("closer == null");
+        }
+        // ...but avoid allocating an allocation stack if "disabled"
+        if (!stackAndTrackingEnabled) {
+            closerNameOrAllocationInfo = closer;
+            return;
+        }
+        String message = "Explicit termination method '" + closer + "' not called";
+        Throwable stack = new Throwable(message);
+        closerNameOrAllocationInfo = stack;
+        Tracker tracker = currentTracker;
+        if (tracker != null) {
+            tracker.open(stack);
+        }
+    }
+
+    // We keep either an allocation stack containing the closer String or, when
+    // in disabled state, just the closer String.
+    // We keep them in a single field only to minimize overhead.
+    private Object /* String or Throwable */ closerNameOrAllocationInfo;
+
+    /**
+     * Marks this CloseGuard instance as closed to avoid warnings on
+     * finalization.
+     */
+    public void close() {
+        Tracker tracker = currentTracker;
+        if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
+            // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
+            tracker.close((Throwable) closerNameOrAllocationInfo);
+        }
+        closerNameOrAllocationInfo = null;
+    }
+
+    /**
+     * Logs a warning if the caller did not properly cleanup by calling an
+     * explicit close method before finalization. If CloseGuard was enabled
+     * when the CloseGuard was created, passes the stacktrace associated with
+     * the allocation to the current reporter. If it was not enabled, it just
+     * directly logs a brief message.
+     */
+    public void warnIfOpen() {
+        if (closerNameOrAllocationInfo != null) {
+            if (closerNameOrAllocationInfo instanceof String) {
+                Log.w("CloseGuard", "A resource failed to call "
+                        + (String) closerNameOrAllocationInfo + ". ");
+            } else {
+                String message =
+                        "A resource was acquired at attached stack trace but never released. ";
+                message += "See java.io.Closeable for information on avoiding resource leaks.";
+                Throwable stack = (Throwable) closerNameOrAllocationInfo;
+                reporter.report(message, stack);
+            }
+        }
+    }
+
+    /**
+     * Interface to allow customization of tracking behaviour.
+     *
+     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+     * MUST NOT be used for any other purposes.
+     */
+    public interface Tracker {
+        void open(Throwable allocationSite);
+        void close(Throwable allocationSite);
+    }
+
+    /**
+     * Interface to allow customization of reporting behavior.
+     * @hide
+     */
+    public interface Reporter {
+        void report(String message, Throwable allocationSite);
+    }
+
+    /**
+     * Default Reporter which reports CloseGuard violations to the log.
+     */
+    private static final class DefaultReporter implements Reporter {
+        private DefaultReporter() {}
+
+        @Override public void report (String message, Throwable allocationSite) {
+            Log.w("CloseGuard", message, allocationSite);
+        }
+    }
+}
diff --git a/media/java/android/media/DataSourceCallback.java b/media/java/android/media/DataSourceCallback.java
index 9b27baf..0d4d531 100644
--- a/media/java/android/media/DataSourceCallback.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 
 /**
- * @hide
  * For supplying media data to the framework. Implement this if your app has
  * special requirements for the way media data is obtained.
  *
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index 702034e..9109ea5 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 
 /**
- * @hide
  * Base class of data source descriptor.
  *
  * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
@@ -36,6 +35,9 @@
 
     // keep consistent with native code
     public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
+    /**
+     * @hide
+     */
     public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;
 
     public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS;
@@ -172,7 +174,8 @@
 
         /**
          * Sets the end position in milliseconds at which the playback will end.
-         * Any negative number is treated as maximum length of the data source.
+         * Any negative number is treated as maximum duration {@link #LONG_MAX_TIME_MS}
+         * of the data source
          *
          * @param position the end position in milliseconds at which the playback will end
          * @return the same Builder instance.
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index 763a81f..14ef180 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -23,7 +23,6 @@
 import java.io.IOException;
 
 /**
- * @hide
  * Structure of data source descriptor for sources using file descriptor.
  *
  * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 284e422..b7743c9 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -17,20 +17,29 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
+import java.util.AbstractSet;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
- * Encapsulates the information describing the format of media data,
- * be it audio or video.
- *
- * The format of the media data is specified as string/value pairs.
- *
+ * Encapsulates the information describing the format of media data, be it audio or video, as
+ * well as optional feature metadata.
+ * <p>
+ * The format of the media data is specified as key/value pairs. Keys are strings. Values can
+ * be integer, long, float, String or ByteBuffer.
+ * <p>
+ * The feature metadata is specificed as string/boolean pairs.
+ * <p>
  * Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:
  *
  * <table>
@@ -938,7 +947,6 @@
      */
     public static final String KEY_CA_SESSION_ID = "ca-session-id";
 
-
     /**
      * A key describing the private data in the CA_descriptor associated with a media track.
      * <p>
@@ -950,7 +958,7 @@
      */
     public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
 
-    /* package private */ MediaFormat(Map<String, Object> map) {
+    /* package private */ MediaFormat(@NonNull Map<String, Object> map) {
         mMap = map;
     }
 
@@ -969,11 +977,58 @@
     /**
      * Returns true iff a key of the given name exists in the format.
      */
-    public final boolean containsKey(String name) {
+    public final boolean containsKey(@NonNull String name) {
         return mMap.containsKey(name);
     }
 
     /**
+     * Returns true iff a feature of the given name exists in the format.
+     */
+    public final boolean containsFeature(@NonNull String name) {
+        return mMap.containsKey(KEY_FEATURE_ + name);
+    }
+
+    public static final int TYPE_NULL = 0;
+    public static final int TYPE_INTEGER = 1;
+    public static final int TYPE_LONG = 2;
+    public static final int TYPE_FLOAT = 3;
+    public static final int TYPE_STRING = 4;
+    public static final int TYPE_BYTE_BUFFER = 5;
+
+    /** @hide */
+    @IntDef({
+        TYPE_NULL,
+        TYPE_INTEGER,
+        TYPE_LONG,
+        TYPE_FLOAT,
+        TYPE_STRING,
+        TYPE_BYTE_BUFFER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /**
+     * Returns the value type for a key. If the key does not exist, it returns TYPE_NULL.
+     */
+    public final @Type int getValueTypeForKey(@NonNull String name) {
+        Object value = mMap.get(name);
+        if (value == null) {
+            return TYPE_NULL;
+        } else if (value instanceof Integer) {
+            return TYPE_INTEGER;
+        } else if (value instanceof Long) {
+            return TYPE_LONG;
+        } else if (value instanceof Float) {
+            return TYPE_FLOAT;
+        } else if (value instanceof String) {
+            return TYPE_STRING;
+        } else if (value instanceof ByteBuffer) {
+            return TYPE_BYTE_BUFFER;
+        }
+        throw new RuntimeException("invalid value for key");
+    }
+
+    /**
      * A key prefix used together with a {@link MediaCodecInfo.CodecCapabilities}
      * feature name describing a required or optional feature for a codec capabilities
      * query.
@@ -989,64 +1044,165 @@
     public static final String KEY_FEATURE_ = "feature-";
 
     /**
-     * Returns the value of an integer key.
+     * Returns the value of a numeric key. This is provided as a convenience method for keys
+     * that may take multiple numeric types, such as {@link #KEY_FRAME_RATE}, or {@link
+     * #KEY_I_FRAME_INTERVAL}.
+     *
+     * @return null if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is ByteBuffer or String
      */
-    public final int getInteger(String name) {
+    public final @Nullable Number getNumber(@NonNull String name) {
+        return ((Number)mMap.get(name));
+    }
+
+    /**
+     * Returns the value of a numeric key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is ByteBuffer or String
+     */
+    public final @NonNull Number getNumber(@NonNull String name, @NonNull Number defaultValue) {
+        Number ret = getNumber(name);
+        return ret == null ? defaultValue : ret;
+    }
+
+    /**
+     * Returns the value of an integer key.
+     *
+     * @throws NullPointerException if the key does not exist or the stored value for the key is
+     *         null
+     * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+     *         String
+     */
+    public final int getInteger(@NonNull String name) {
         return ((Integer)mMap.get(name)).intValue();
     }
 
     /**
-     * Returns the value of an integer key, or the default value if the
-     * key is missing or is for another type value.
-     * @hide
+     * Returns the value of an integer key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+     *         String
      */
-    public final int getInteger(String name, int defaultValue) {
+    public final int getInteger(@NonNull String name, int defaultValue) {
         try {
             return getInteger(name);
+        } catch (NullPointerException  e) {
+            /* no such field or field is null */
+            return defaultValue;
         }
-        catch (NullPointerException  e) { /* no such field */ }
-        catch (ClassCastException e) { /* field of different type */ }
-        return defaultValue;
     }
 
     /**
      * Returns the value of a long key.
+     *
+     * @throws NullPointerException if the key does not exist or the stored value for the key is
+     *         null
+     * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+     *         String
      */
-    public final long getLong(String name) {
+    public final long getLong(@NonNull String name) {
         return ((Long)mMap.get(name)).longValue();
     }
 
     /**
-     * Returns the value of a float key.
+     * Returns the value of an long key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+     *         String
      */
-    public final float getFloat(String name) {
+    public final long getLong(@NonNull String name, long defaultValue) {
+        try {
+            return getLong(name);
+        } catch (NullPointerException  e) {
+            /* no such field or field is null */
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value of a float key.
+     *
+     * @throws NullPointerException if the key does not exist or the stored value for the key is
+     *         null
+     * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+     *         String
+     */
+    public final float getFloat(@NonNull String name) {
         return ((Float)mMap.get(name)).floatValue();
     }
 
     /**
-     * Returns the value of a string key.
+     * Returns the value of an float key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+     *         String
      */
-    public final String getString(String name) {
+    public final float getFloat(@NonNull String name, float defaultValue) {
+        try {
+            return getFloat(name);
+        } catch (NullPointerException  e) {
+            /* no such field or field is null */
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value of a string key.
+     *
+     * @return null if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
+     */
+    public final @Nullable String getString(@NonNull String name) {
         return (String)mMap.get(name);
     }
 
     /**
-     * Returns the value of a ByteBuffer key.
+     * Returns the value of an string key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
      */
-    public final ByteBuffer getByteBuffer(String name) {
+    public final @NonNull String getString(@NonNull String name, @NonNull String defaultValue) {
+        String ret = getString(name);
+        return ret == null ? defaultValue : ret;
+    }
+
+    /**
+     * Returns the value of a ByteBuffer key.
+     *
+     * @return null if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or String
+     */
+    public final @Nullable ByteBuffer getByteBuffer(@NonNull String name) {
         return (ByteBuffer)mMap.get(name);
     }
 
     /**
+     * Returns the value of a ByteBuffer key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or String
+     */
+    public final @NonNull ByteBuffer getByteBuffer(
+            @NonNull String name, @NonNull ByteBuffer defaultValue) {
+        ByteBuffer ret = getByteBuffer(name);
+        return ret == null ? defaultValue : ret;
+    }
+
+    /**
      * Returns whether a feature is to be enabled ({@code true}) or disabled
      * ({@code false}).
      *
      * @param feature the name of a {@link MediaCodecInfo.CodecCapabilities} feature.
      *
      * @throws IllegalArgumentException if the feature was neither set to be enabled
-     *        nor to be disabled.
+     *         nor to be disabled.
      */
-    public boolean getFeatureEnabled(String feature) {
+    public boolean getFeatureEnabled(@NonNull String feature) {
         Integer enabled = (Integer)mMap.get(KEY_FEATURE_ + feature);
         if (enabled == null) {
             throw new IllegalArgumentException("feature is not specified");
@@ -1057,39 +1213,239 @@
     /**
      * Sets the value of an integer key.
      */
-    public final void setInteger(String name, int value) {
+    public final void setInteger(@NonNull String name, int value) {
         mMap.put(name, Integer.valueOf(value));
     }
 
     /**
      * Sets the value of a long key.
      */
-    public final void setLong(String name, long value) {
+    public final void setLong(@NonNull String name, long value) {
         mMap.put(name, Long.valueOf(value));
     }
 
     /**
      * Sets the value of a float key.
      */
-    public final void setFloat(String name, float value) {
+    public final void setFloat(@NonNull String name, float value) {
         mMap.put(name, new Float(value));
     }
 
     /**
      * Sets the value of a string key.
+     * <p>
+     * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+     * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+     * remove a key.
      */
-    public final void setString(String name, String value) {
+    public final void setString(@NonNull String name, @Nullable String value) {
         mMap.put(name, value);
     }
 
     /**
      * Sets the value of a ByteBuffer key.
+     * <p>
+     * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+     * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+     * remove a key.
      */
-    public final void setByteBuffer(String name, ByteBuffer bytes) {
+    public final void setByteBuffer(@NonNull String name, @Nullable ByteBuffer bytes) {
         mMap.put(name, bytes);
     }
 
     /**
+     * Removes a value of a given key if present. Has no effect if the key is not present.
+     */
+    public final void removeKey(@NonNull String name) {
+        // exclude feature mappings
+        if (!name.startsWith(KEY_FEATURE_)) {
+            mMap.remove(name);
+        }
+    }
+
+    /**
+     * Removes a given feature setting if present. Has no effect if the feature setting is not
+     * present.
+     */
+    public final void removeFeature(@NonNull String name) {
+        mMap.remove(KEY_FEATURE_ + name);
+    }
+
+    /**
+     * A Partial set view for a portion of the keys in a MediaFormat object.
+     *
+     * This class is needed as we want to return a portion of the actual format keys in getKeys()
+     * and another portion of the keys in getFeatures(), and still allow the view properties.
+     */
+    private abstract class FilteredMappedKeySet extends AbstractSet<String> {
+        private Set<String> mKeys;
+
+        // Returns true if this set should include this key
+        abstract protected boolean keepKey(String key);
+
+        // Maps a key from the underlying key set into its new value in this key set
+        abstract protected String mapKeyToItem(String key);
+
+        // Maps a key from this key set into its original value in the underlying key set
+        abstract protected String mapItemToKey(String item);
+
+        public FilteredMappedKeySet() {
+            mKeys = mMap.keySet();
+        }
+
+        // speed up contains and remove from abstract implementation (that would iterate
+        // over each element)
+        @Override
+        public boolean contains(Object o) {
+            if (o instanceof String) {
+                String key = mapItemToKey((String)o);
+                return keepKey(key) && mKeys.contains(key);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            if (o instanceof String) {
+                String key = mapItemToKey((String)o);
+                if (keepKey(key) && mKeys.remove(key)) {
+                    mMap.remove(key);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private class KeyIterator implements Iterator<String> {
+            Iterator<String> mIterator;
+            String mLast;
+
+            public KeyIterator() {
+                // We must create a copy of the filtered stream, as remove operation has to modify
+                // the underlying data structure (mMap), so the iterator's operation is undefined.
+                // Use a list as it is likely less memory consuming than the other alternative: set.
+                mIterator =
+                    mKeys.stream().filter(k -> keepKey(k)).collect(Collectors.toList()).iterator();
+            }
+
+            @Override
+            public boolean hasNext() {
+                return mIterator.hasNext();
+            }
+
+            @Override
+            public String next() {
+                mLast = mIterator.next();
+                return mapKeyToItem(mLast);
+            }
+
+            @Override
+            public void remove() {
+                mIterator.remove();
+                mMap.remove(mLast);
+            }
+        }
+
+        @Override
+        public Iterator<String> iterator() {
+            return new KeyIterator();
+        }
+
+        @Override
+        public int size() {
+            return (int)mKeys.stream().filter(k -> keepKey(k)).count();
+        }
+    }
+
+    /**
+     * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+     * don't start with a prefix, such as "feature-"
+     */
+    private class UnprefixedKeySet extends FilteredMappedKeySet {
+        private String mPrefix;
+
+        public UnprefixedKeySet(String prefix) {
+            super();
+            mPrefix = prefix;
+        }
+
+        protected boolean keepKey(String key) {
+            return !key.startsWith(mPrefix);
+        }
+
+        protected String mapKeyToItem(String key) {
+            return key;
+        }
+
+        protected String mapItemToKey(String item) {
+            return item;
+        }
+    }
+
+    /**
+     * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+     * start with a prefix, such as "feature-", with the prefix removed
+     */
+    private class PrefixedKeySetWithPrefixRemoved extends FilteredMappedKeySet {
+        private String mPrefix;
+        private int mPrefixLength;
+
+        public PrefixedKeySetWithPrefixRemoved(String prefix) {
+            super();
+            mPrefix = prefix;
+            mPrefixLength = prefix.length();
+        }
+
+        protected boolean keepKey(String key) {
+            return key.startsWith(mPrefix);
+        }
+
+        protected String mapKeyToItem(String key) {
+            return key.substring(mPrefixLength);
+        }
+
+        protected String mapItemToKey(String item) {
+            return mPrefix + item;
+        }
+    }
+
+
+   /**
+     * Returns a {@link java.util.Set Set} view of the keys contained in this MediaFormat.
+     *
+     * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+     * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+     * (except through the iterator's own remove operation), the results of the iteration are
+     * undefined. The set supports element removal, which removes the corresponding mapping from the
+     * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+     * It does not support the add or addAll operations.
+     */
+    public final @NonNull java.util.Set<String> getKeys() {
+        return new UnprefixedKeySet(KEY_FEATURE_);
+    }
+
+   /**
+     * Returns a {@link java.util.Set Set} view of the features contained in this MediaFormat.
+     *
+     * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+     * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+     * (except through the iterator's own remove operation), the results of the iteration are
+     * undefined. The set supports element removal, which removes the corresponding mapping from the
+     * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+     * It does not support the add or addAll operations.
+     */
+    public final @NonNull java.util.Set<String> getFeatures() {
+        return new PrefixedKeySetWithPrefixRemoved(KEY_FEATURE_);
+    }
+
+    /**
+     * Create a copy of a media format object.
+     */
+    public MediaFormat(@NonNull MediaFormat other) {
+        mMap.putAll(other.mMap);
+    }
+
+    /**
      * Sets whether a feature is to be enabled ({@code true}) or disabled
      * ({@code false}).
      *
@@ -1102,7 +1458,7 @@
      * @see MediaCodecList#findEncoderForFormat
      * @see MediaCodecInfo.CodecCapabilities#isFormatSupported
      */
-    public void setFeatureEnabled(String feature, boolean enabled) {
+    public void setFeatureEnabled(@NonNull String feature, boolean enabled) {
         setInteger(KEY_FEATURE_ + feature, enabled ? 1 : 0);
     }
 
@@ -1112,8 +1468,8 @@
      * @param sampleRate The sampling rate of the content.
      * @param channelCount The number of audio channels in the content.
      */
-    public static final MediaFormat createAudioFormat(
-            String mime,
+    public static final @NonNull MediaFormat createAudioFormat(
+            @NonNull String mime,
             int sampleRate,
             int channelCount) {
         MediaFormat format = new MediaFormat();
@@ -1132,8 +1488,8 @@
      *        in the content.  (This will also work if there are multiple language
      *        tracks in the content.)
      */
-    public static final MediaFormat createSubtitleFormat(
-            String mime,
+    public static final @NonNull MediaFormat createSubtitleFormat(
+            @NonNull String mime,
             String language) {
         MediaFormat format = new MediaFormat();
         format.setString(KEY_MIME, mime);
@@ -1148,8 +1504,8 @@
      * @param width The width of the content (in pixels)
      * @param height The height of the content (in pixels)
      */
-    public static final MediaFormat createVideoFormat(
-            String mime,
+    public static final @NonNull MediaFormat createVideoFormat(
+            @NonNull String mime,
             int width,
             int height) {
         MediaFormat format = new MediaFormat();
@@ -1161,7 +1517,7 @@
     }
 
     @Override
-    public String toString() {
+    public @NonNull String toString() {
         return mMap.toString();
     }
 }
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 111dd0f..e6ad444 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -21,9 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -48,8 +45,6 @@
 import com.android.framework.protobuf.InvalidProtocolBufferException;
 import com.android.internal.annotations.GuardedBy;
 
-import dalvik.system.CloseGuard;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -83,8 +78,6 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 /**
- * @hide
- *
  * MediaPlayer2 class can be used to control playback of audio/video files and streams.
  *
  * <p>Topics covered here are:
@@ -778,7 +771,7 @@
      *
      * @return the current DataSourceDesc
      */
-    public DataSourceDesc getCurrentDataSource() {
+    public @Nullable DataSourceDesc getCurrentDataSource() {
         synchronized (mSrcLock) {
             return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD;
         }
@@ -1254,19 +1247,18 @@
      *
      * <p>This function has the MediaPlayer2 access the low-level power manager
      * service to control the device's power usage while playing is occurring.
-     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
+     * The parameter is a {@link android.os.PowerManager.WakeLock}.
      * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
      * permission.
      * By default, no attempt is made to keep the device awake during playback.
      *
-     * @param context the Context to use
-     * @param mode    the power/wake mode to set
+     * @param wakeLock the power wake lock used during playback.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      * @see android.os.PowerManager
      */
     // This is an asynchronous call.
-    public Object setWakeMode(Context context, int mode) {
-        return addTask(new Task(CALL_COMPLETED_SET_WAKE_MODE, false) {
+    public Object setWakeLock(@NonNull PowerManager.WakeLock wakeLock) {
+        return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
             @Override
             void process() {
                 boolean washeld = false;
@@ -1276,28 +1268,15 @@
                         washeld = true;
                         mWakeLock.release();
                     }
-                    mWakeLock = null;
                 }
 
-                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-                ActivityManager am =
-                        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-                List<RunningAppProcessInfo> runningAppsProcInfo = am.getRunningAppProcesses();
-                int pid = android.os.Process.myPid();
-                String name = "pid " + String.valueOf(pid);
-                if (runningAppsProcInfo != null) {
-                    for (RunningAppProcessInfo procInfo : runningAppsProcInfo) {
-                        if (procInfo.pid == pid) {
-                            name = procInfo.processName;
-                            break;
-                        }
+                mWakeLock = wakeLock;
+                if (mWakeLock != null) {
+                    mWakeLock.setReferenceCounted(false);
+                    if (washeld) {
+                        mWakeLock.acquire();
                     }
                 }
-                mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, name);
-                mWakeLock.setReferenceCounted(false);
-                if (washeld) {
-                    mWakeLock.acquire();
-                }
             }
         });
     }
@@ -1305,7 +1284,7 @@
     /**
      * Control whether we should use the attached SurfaceHolder to keep the
      * screen on while video playback is occurring.  This is the preferred
-     * method over {@link #setWakeMode} where possible, since it doesn't
+     * method over {@link #setWakeLock} where possible, since it doesn't
      * require that the application have permission for low-level wake lock
      * access.
      *
@@ -1352,9 +1331,13 @@
      *
      * @param token the command to be canceled. This is the returned Object when command is issued.
      * @return {@code false} if the task could not be cancelled; {@code true} otherwise.
+     * @throws IllegalArgumentException if argument token is null.
      */
     // This is a synchronous call.
-    public boolean cancelCommand(Object token) {
+    public boolean cancelCommand(@NonNull Object token) {
+        if (token == null) {
+            throw new IllegalArgumentException("command token should not be null");
+        }
         synchronized (mTaskLock) {
             return mPendingTasks.remove(token);
         }
@@ -1893,7 +1876,6 @@
          * Gets the track type.
          * @return TrackType which indicates if the track is video, audio, timed text.
          */
-        @UnsupportedAppUsage
         public int getTrackType() {
             return mTrackType;
         }
@@ -1904,7 +1886,6 @@
          * When the language is unknown or could not be determined,
          * ISO-639-2 language code, "und", is returned.
          */
-        @UnsupportedAppUsage
         public String getLanguage() {
             String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
             return language == null ? "und" : language;
@@ -1935,19 +1916,20 @@
         final int mTrackType;
         final MediaFormat mFormat;
 
-        TrackInfo(Iterator<Value> in) {
-            mTrackType = in.next().getInt32Value();
+        static TrackInfo create(Iterator<Value> in) {
+            int trackType = in.next().getInt32Value();
             // TODO: build the full MediaFormat; currently we are using createSubtitleFormat
             // even for audio/video tracks, meaning we only set the mime and language.
             String mime = in.next().getStringValue();
             String language = in.next().getStringValue();
-            mFormat = MediaFormat.createSubtitleFormat(mime, language);
+            MediaFormat format = MediaFormat.createSubtitleFormat(mime, language);
 
-            if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-                mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value());
-                mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
-                mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
+            if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
+                format.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value());
+                format.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
+                format.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
             }
+            return new TrackInfo(trackType, format);
         }
 
         /** @hide */
@@ -1992,9 +1974,9 @@
      * addTimedTextSource method is called.
      * @throws IllegalStateException if it is called in an invalid state.
      */
-    public List<TrackInfo> getTrackInfo() {
+    public @NonNull List<TrackInfo> getTrackInfo() {
         TrackInfo[] trackInfo = getInbandTrackInfo();
-        return Arrays.asList(trackInfo);
+        return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
     }
 
     private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
@@ -2012,7 +1994,7 @@
         }
         TrackInfo[] trackInfo = new TrackInfo[size];
         for (int i = 0; i < size; ++i) {
-            trackInfo[i] = new TrackInfo(in);
+            trackInfo[i] = TrackInfo.create(in);
         }
         return trackInfo;
     }
@@ -2507,7 +2489,7 @@
                         Log.w(TAG, "MEDIA_DRM_INFO failed to parse msg.obj " + obj);
                         break;
                     }
-                    DrmInfo drmInfo = new DrmInfo(playerMsg);
+                    DrmInfo drmInfo = DrmInfo.create(playerMsg);
                     synchronized (sourceInfo) {
                         sourceInfo.mDrmInfo = drmInfo;
                     }
@@ -2558,7 +2540,7 @@
          * @param size the size of the video
          */
         public void onVideoSizeChanged(
-                MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) { }
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull VideoSize size) { }
 
         /**
          * Called to indicate an avaliable timed text
@@ -2569,7 +2551,8 @@
          *             needed to be displayed and the display format.
          * @hide
          */
-        public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) { }
+        public void onTimedText(
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull TimedText text) { }
 
         /**
          * Called to indicate avaliable timed metadata
@@ -2590,7 +2573,8 @@
          * @param data the timed metadata sample associated with this event
          */
         public void onTimedMetaDataAvailable(
-                MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { }
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull TimedMetaData data) { }
 
         /**
          * Called to indicate an error.
@@ -2602,7 +2586,8 @@
          * implementation dependent.
          */
         public void onError(
-                MediaPlayer2 mp, DataSourceDesc dsd, @MediaError int what, int extra) { }
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @MediaError int what, int extra) { }
 
         /**
          * Called to indicate an info or a warning.
@@ -2613,7 +2598,9 @@
          * @param extra an extra code, specific to the info. Typically
          * implementation dependent.
          */
-        public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, @MediaInfo int what, int extra) { }
+        public void onInfo(
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @MediaInfo int what, int extra) { }
 
         /**
          * Called to acknowledge an API call.
@@ -2624,7 +2611,7 @@
          * @param status the returned status code for the call.
          */
         public void onCallCompleted(
-                MediaPlayer2 mp, DataSourceDesc dsd, @CallCompleted int what,
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @CallCompleted int what,
                 @CallStatus int status) { }
 
         /**
@@ -2635,7 +2622,8 @@
          * @param timestamp the new media clock.
          */
         public void onMediaTimeDiscontinuity(
-                MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaTimestamp timestamp) { }
 
         /**
          * Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed.
@@ -2644,7 +2632,7 @@
          * @param label the application specific Object given by
          *        {@link #notifyWhenCommandLabelReached(Object)}.
          */
-        public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { }
+        public void onCommandLabelReached(@NonNull MediaPlayer2 mp, @NonNull Object label) { }
 
         /**
          * Called when when a player subtitle track has new subtitle data available.
@@ -2653,7 +2641,8 @@
          * @param data the subtitle data
          */
         public void onSubtitleData(
-                MediaPlayer2 mp, DataSourceDesc dsd, @NonNull SubtitleData data) { }
+                @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull SubtitleData data) { }
     }
 
     private final Object mEventCbLock = new Object();
@@ -3048,10 +3037,10 @@
      */
     public static final int CALL_COMPLETED_SET_DISPLAY = 33;
 
-    /** The player just completed a call {@link #setWakeMode}.
+    /** The player just completed a call {@link #setWakeLock}.
      * @see EventCallback#onCallCompleted
      */
-    public static final int CALL_COMPLETED_SET_WAKE_MODE = 34;
+    public static final int CALL_COMPLETED_SET_WAKE_LOCK = 34;
 
     /** The player just completed a call {@link #setScreenOnWhilePlaying}.
      * @see EventCallback#onCallCompleted
@@ -3104,7 +3093,7 @@
             CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
             CALL_COMPLETED_SET_BUFFERING_PARAMS,
             CALL_COMPLETED_SET_DISPLAY,
-            CALL_COMPLETED_SET_WAKE_MODE,
+            CALL_COMPLETED_SET_WAKE_LOCK,
             CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING,
             CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
             CALL_COMPLETED_PREPARE_DRM,
@@ -3181,6 +3170,7 @@
      * The only allowed DRM calls in this listener are
      * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)}
      * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}.
+     * @hide
      */
     public interface OnDrmConfigHelper {
         /**
@@ -3199,6 +3189,7 @@
      * of {@link #prepareDrm(DataSourceDesc, UUID)}.
      *
      * @param listener the callback that will be run
+     * @hide
      */
     // This is a synchronous call.
     public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
@@ -3210,6 +3201,7 @@
     /**
      * Interface definition for callbacks to be invoked when the player has the corresponding
      * DRM events.
+     * @hide
      */
     public static class DrmEventCallback {
         /**
@@ -3243,6 +3235,7 @@
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
+     * @hide
      */
     // This is a synchronous call.
     public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@@ -3263,6 +3256,7 @@
      * Unregisters the {@link DrmEventCallback}.
      *
      * @param eventCallback the callback to be unregistered
+     * @hide
      */
     // This is a synchronous call.
     public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
@@ -3280,31 +3274,37 @@
      * <p>
      *
      * DRM preparation has succeeded.
+     * @hide
      */
     public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
 
     /**
      * The device required DRM provisioning but couldn't reach the provisioning server.
+     * @hide
      */
     public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
 
     /**
      * The device required DRM provisioning but the provisioning server denied the request.
+     * @hide
      */
     public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
 
     /**
      * The DRM preparation has failed .
+     * @hide
      */
     public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
 
     /**
      * The crypto scheme UUID is not supported by the device.
+     * @hide
      */
     public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4;
 
     /**
      * The hardware resources are not available, due to being in use.
+     * @hide
      */
     public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
 
@@ -3345,6 +3345,7 @@
      * @param dsd The DRM protected data source
      *
      * @throws IllegalStateException if called before being prepared
+     * @hide
      */
     public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
         final SourceInfo sourceInfo = getSourceInfo(dsd);
@@ -3400,6 +3401,7 @@
      * {@link DrmEventCallback#onDrmInfo}.
      *
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
+     * @hide
      */
     // This is an asynchronous call.
     public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
@@ -3493,6 +3495,7 @@
      * @param dsd The DRM protected data source
      *
      * @throws NoDrmSchemeException if there is no active DRM session to release
+     * @hide
      */
     // This is a synchronous call.
     public void releaseDrm(@NonNull DataSourceDesc dsd)
@@ -3503,7 +3506,7 @@
         }
     }
 
-    private native void native_releaseDrm();
+    private native void native_releaseDrm(long mSrcId);
 
     /**
      * A key request/response exchange occurs between the app and a license server
@@ -3543,6 +3546,7 @@
      * This may be {@code null} if no additional parameters are to be sent.
      *
      * @throws NoDrmSchemeException if there is no active DRM session
+     * @hide
      */
     public MediaDrm.KeyRequest getDrmKeyRequest(
             @NonNull DataSourceDesc dsd,
@@ -3583,6 +3587,7 @@
      * @throws NoDrmSchemeException if there is no active DRM session
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
+     * @hide
      */
     // This is a synchronous call.
     public byte[] provideDrmKeyResponse(
@@ -3608,6 +3613,7 @@
      * @param keySetId identifies the saved key set to restore
      *
      * @throws NoDrmSchemeException if there is no active DRM session
+     * @hide
      */
     // This is a synchronous call.
     public void restoreDrmKeys(
@@ -3635,6 +3641,7 @@
      * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
      *
      * @throws NoDrmSchemeException if there is no active DRM session
+     * @hide
      */
     public String getDrmPropertyString(
             @NonNull DataSourceDesc dsd,
@@ -3661,6 +3668,7 @@
      * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
      *
      * @throws NoDrmSchemeException if there is no active DRM session
+     * @hide
      */
     // This is a synchronous call.
     public void setDrmPropertyString(
@@ -3678,6 +3686,7 @@
 
     /**
      * Encapsulates the DRM properties of the source.
+     * @hide
      */
     public static final class DrmInfo {
         private Map<UUID, byte[]> mMapPssh;
@@ -3704,36 +3713,37 @@
             mSupportedSchemes = supportedSchemes;
         }
 
-        private DrmInfo(PlayerMessage msg) {
-            Log.v(TAG, "DrmInfo(" + msg + ")");
+        private static DrmInfo create(PlayerMessage msg) {
+            Log.v(TAG, "DrmInfo.create(" + msg + ")");
 
             Iterator<Value> in = msg.getValuesList().iterator();
             byte[] pssh = in.next().getBytesValue().toByteArray();
 
-            Log.v(TAG, "DrmInfo() PSSH: " + arrToHex(pssh));
-            mMapPssh = parsePSSH(pssh, pssh.length);
-            Log.v(TAG, "DrmInfo() PSSH: " + mMapPssh);
+            Log.v(TAG, "DrmInfo.create() PSSH: " + DrmInfo.arrToHex(pssh));
+            Map<UUID, byte[]> mapPssh = DrmInfo.parsePSSH(pssh, pssh.length);
+            Log.v(TAG, "DrmInfo.create() PSSH: " + mapPssh);
 
             int supportedDRMsCount = in.next().getInt32Value();
-            mSupportedSchemes = new UUID[supportedDRMsCount];
+            UUID[] supportedSchemes = new UUID[supportedDRMsCount];
             for (int i = 0; i < supportedDRMsCount; i++) {
                 byte[] uuid = new byte[16];
                 in.next().getBytesValue().copyTo(uuid, 0);
 
-                mSupportedSchemes[i] = bytesToUUID(uuid);
+                supportedSchemes[i] = DrmInfo.bytesToUUID(uuid);
 
-                Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + mSupportedSchemes[i]);
+                Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + supportedSchemes[i]);
             }
 
-            Log.v(TAG, "DrmInfo() psshsize: " + pssh.length
+            Log.v(TAG, "DrmInfo.create() psshsize: " + pssh.length
                     + " supportedDRMsCount: " + supportedDRMsCount);
+            return new DrmInfo(mapPssh, supportedSchemes);
         }
 
         private DrmInfo makeCopy() {
             return new DrmInfo(this.mMapPssh, this.mSupportedSchemes);
         }
 
-        private String arrToHex(byte[] bytes) {
+        private static String arrToHex(byte[] bytes) {
             String out = "0x";
             for (int i = 0; i < bytes.length; i++) {
                 out += String.format("%02x", bytes[i]);
@@ -3742,7 +3752,7 @@
             return out;
         }
 
-        private UUID bytesToUUID(byte[] uuid) {
+        private static UUID bytesToUUID(byte[] uuid) {
             long msb = 0, lsb = 0;
             for (int i = 0; i < 8; i++) {
                 msb |= (((long) uuid[i]     & 0xff) << (8 * (7 - i)));
@@ -3752,7 +3762,7 @@
             return new UUID(msb, lsb);
         }
 
-        private Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) {
+        private static Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) {
             Map<UUID, byte[]> result = new HashMap<UUID, byte[]>();
 
             final int uuidSize = 16;
@@ -3816,6 +3826,7 @@
      * Thrown when a DRM method is called before preparing a DRM scheme through
      * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}.
      * Extends MediaDrm.MediaDrmException
+     * @hide
      */
     public static final class NoDrmSchemeException extends MediaDrmException {
         public NoDrmSchemeException(String detailMessage) {
@@ -3823,7 +3834,8 @@
         }
     }
 
-    private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
+    private native void native_prepareDrm(
+            long srcId, @NonNull byte[] uuid, @NonNull byte[] drmSessionId);
 
     // Instantiated from the native side
     @SuppressWarnings("unused")
@@ -4066,6 +4078,7 @@
         static final int PROVISION_TIMEOUT_MS = 60000;
 
         final DataSourceDesc mDSD;
+        final long mSrcId;
 
         //--- guarded by |this| start
         MediaDrm mDrmObj;
@@ -4077,8 +4090,9 @@
         Future<?> mProvisionResult;
         //--- guarded by |this| end
 
-        DrmHandle(DataSourceDesc dsd) {
+        DrmHandle(DataSourceDesc dsd, long srcId) {
             mDSD = dsd;
+            mSrcId = srcId;
         }
 
         void prepare(UUID uuid) throws UnsupportedSchemeException,
@@ -4188,7 +4202,8 @@
 
                 // Sending it down to native/mediaserver to create the crypto object
                 // This call could simply fail due to bad player state, e.g., after play().
-                MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
+                final MediaPlayer2 mp2 = MediaPlayer2.this;
+                mp2.native_prepareDrm(mSrcId, getByteArrayFromUUID(uuid), mDrmSessionId);
                 Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
 
             } catch (Exception e) { //ResourceBusyException, NotProvisionedException
@@ -4369,7 +4384,7 @@
                     // exception if we're in a non-stopped/prepared state.
 
                     // for cleaning native/mediaserver crypto object
-                    native_releaseDrm();
+                    native_releaseDrm(mSrcId);
 
                     // for cleaning client-side MediaDrm object; only called if above has succeeded
                     cleanDrmObj();
@@ -4575,7 +4590,7 @@
 
         SourceInfo(DataSourceDesc dsd) {
             this.mDSD = dsd;
-            mDrmHandle = new DrmHandle(dsd);
+            mDrmHandle = new DrmHandle(dsd, mId);
         }
 
         void close() {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0950a24..90cfc53 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1015,12 +1015,6 @@
                     // exif is null
                 }
                 if (exif != null) {
-                    float[] latlng = new float[2];
-                    if (exif.getLatLong(latlng)) {
-                        values.put(Images.Media.LATITUDE, latlng[0]);
-                        values.put(Images.Media.LONGITUDE, latlng[1]);
-                    }
-
                     long time = exif.getGpsDateTime();
                     if (time != -1) {
                         values.put(Images.Media.DATE_TAKEN, time);
diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java
index ba37b9b..852babe 100644
--- a/media/java/android/media/SubtitleData.java
+++ b/media/java/android/media/SubtitleData.java
@@ -17,8 +17,11 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 
+import java.util.Arrays;
+
 /**
  * Class encapsulating subtitle data, as received through the
  * {@link MediaPlayer.OnSubtitleDataListener} interface.
@@ -80,11 +83,11 @@
     }
 
     /** @hide */
-    public SubtitleData(int trackIndex, long startTimeUs, long durationUs, byte[] data) {
+    public SubtitleData(int trackIndex, long startTimeUs, long durationUs, @NonNull byte[] data) {
         mTrackIndex = trackIndex;
         mStartTimeUs = startTimeUs;
         mDurationUs = durationUs;
-        mData = data;
+        mData = (data != null ? data : new byte[0]);
     }
 
     /**
@@ -138,4 +141,80 @@
 
         return true;
     }
+
+    /**
+     * Builder class for {@link SubtitleData} objects.
+     * <p> Here is an example where <code>Builder</code> is used to define the
+     * {@link SubtitleData}:
+     *
+     * <pre class="prettyprint">
+     * SubtitleData sd = new SubtitleData.Builder()
+     *         .setSubtitleData(trackIndex, startTime, duration, data)
+     *         .build();
+     * </pre>
+     * @hide
+     */
+    @SystemApi
+    public static class Builder {
+        private int mTrackIndex;
+        private long mStartTimeUs;
+        private long mDurationUs;
+        private byte[] mData = new byte[0];
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given {@link SubtitleData} instance
+         * @param sd the {@link SubtitleData} object whose data will be reused
+         * in the new Builder. It should not be null. The data array is copied.
+         */
+        public Builder(@NonNull SubtitleData sd) {
+            if (sd == null) {
+                throw new IllegalArgumentException("null SubtitleData is not allowed");
+            }
+            mTrackIndex = sd.mTrackIndex;
+            mStartTimeUs = sd.mStartTimeUs;
+            mDurationUs = sd.mDurationUs;
+            if (sd.mData != null) {
+                mData = Arrays.copyOf(sd.mData, sd.mData.length);
+            }
+        }
+
+        /**
+         * Combines all of the fields that have been set and return a new
+         * {@link SubtitleData} object. <code>IllegalStateException</code> will be
+         * thrown if there is conflict between fields.
+         *
+         * @return a new {@link SubtitleData} object
+         */
+        public @NonNull SubtitleData build() {
+            return new SubtitleData(mTrackIndex, mStartTimeUs, mDurationUs, mData);
+        }
+
+        /**
+         * Sets the info of subtitle data.
+         *
+         * @param trackIndex the ParcelFileDescriptor for the file to play
+         * @param startTimeUs the start time in microsecond for the subtile data
+         * @param durationUs the duration in microsecond for the subtile data
+         * @param data the data array for the subtile data. It should not be null.
+         *     No data copying is made.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setSubtitleData(
+                int trackIndex, long startTimeUs, long durationUs, @NonNull byte[] data) {
+            if (data == null) {
+                throw new IllegalArgumentException("null data is not allowed");
+            }
+            mTrackIndex = trackIndex;
+            mStartTimeUs = startTimeUs;
+            mDurationUs = durationUs;
+            mData = data;
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/TimedMetaData.java b/media/java/android/media/TimedMetaData.java
index 97e6bfa..bcc18ef 100644
--- a/media/java/android/media/TimedMetaData.java
+++ b/media/java/android/media/TimedMetaData.java
@@ -16,8 +16,12 @@
 
 package android.media;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 
+import java.util.Arrays;
+
 /**
  * Class that embodies one timed metadata access unit, including
  *
@@ -50,7 +54,10 @@
     /**
      * @hide
      */
-    public TimedMetaData(long timestampUs, byte[] metaData) {
+    public TimedMetaData(long timestampUs, @NonNull byte[] metaData) {
+        if (metaData == null) {
+            throw new IllegalArgumentException("null metaData is not allowed");
+        }
         mTimestampUs = timestampUs;
         mMetaData = metaData;
     }
@@ -83,4 +90,71 @@
 
         return true;
     }
+
+    /**
+     * Builder class for {@link TimedMetaData} objects.
+     * <p> Here is an example where <code>Builder</code> is used to define the
+     * {@link TimedMetaData}:
+     *
+     * <pre class="prettyprint">
+     * TimedMetaData tmd = new TimedMetaData.Builder()
+     *         .setTimedMetaData(timestamp, metaData)
+     *         .build();
+     * </pre>
+     * @hide
+     */
+    @SystemApi
+    public static class Builder {
+        private long mTimestampUs;
+        private byte[] mMetaData = new byte[0];
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given {@link TimedMetaData} instance
+         * @param tmd the {@link TimedMetaData} object whose data will be reused
+         * in the new Builder. It should not be null. The metadata array is copied.
+         */
+        public Builder(@NonNull TimedMetaData tmd) {
+            if (tmd == null) {
+                throw new IllegalArgumentException("null TimedMetaData is not allowed");
+            }
+            mTimestampUs = tmd.mTimestampUs;
+            if (tmd.mMetaData != null) {
+                mMetaData = Arrays.copyOf(tmd.mMetaData, tmd.mMetaData.length);
+            }
+        }
+
+        /**
+         * Combines all of the fields that have been set and return a new
+         * {@link TimedMetaData} object. <code>IllegalStateException</code> will be
+         * thrown if there is conflict between fields.
+         *
+         * @return a new {@link TimedMetaData} object
+         */
+        public @NonNull TimedMetaData build() {
+            return new TimedMetaData(mTimestampUs, mMetaData);
+        }
+
+        /**
+         * Sets the info of timed metadata.
+         *
+         * @param timestamp the timestamp in microsecond for the timed metadata
+         * @param metaData the metadata array for the timed metadata. No data copying is made.
+         *     It should not be null.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setTimedMetaData(int timestamp, @NonNull byte[] metaData) {
+            if (metaData == null) {
+                throw new IllegalArgumentException("null metaData is not allowed");
+            }
+            mTimestampUs = timestamp;
+            mMetaData = metaData;
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java
index 6a83dab..e39f53c 100644
--- a/media/java/android/media/UriDataSourceDesc.java
+++ b/media/java/android/media/UriDataSourceDesc.java
@@ -30,7 +30,6 @@
 import java.util.Map;
 
 /**
- * @hide
  * Structure of data source descriptor for sources using URI.
  *
  * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
diff --git a/media/java/android/media/VideoSize.java b/media/java/android/media/VideoSize.java
index 7e5cb1f..19631e0 100644
--- a/media/java/android/media/VideoSize.java
+++ b/media/java/android/media/VideoSize.java
@@ -18,8 +18,6 @@
 
 /**
  * Immutable class for describing width and height dimensions.
- *
- * @hide
  */
 public final class VideoSize {
     /**
@@ -28,7 +26,7 @@
      * @param width The width of the video size
      * @param height The height of the video size
      */
-    public VideoSize(int width, int height) {
+    VideoSize(int width, int height) {
         mWidth = width;
         mHeight = height;
     }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8215779..d91cf87 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,9 +96,15 @@
      * @return The binder object from the system
      * @hide
      */
+    @SystemApi
     public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
-            @NonNull String tag, int userId) throws RemoteException {
-        return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+            @NonNull String tag, int userId) {
+        try {
+            return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null;
     }
 
     /**
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 4567492..8b6009e 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1192,7 +1192,7 @@
 }
 
 static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz,
-                    jbyteArray uuidObj, jbyteArray drmSessionIdObj)
+                    jlong srcId, jbyteArray uuidObj, jbyteArray drmSessionIdObj)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL) {
@@ -1225,7 +1225,7 @@
         return;
     }
 
-    status_t err = mp->prepareDrm(uuid.array(), drmSessionId);
+    status_t err = mp->prepareDrm(srcId, uuid.array(), drmSessionId);
     if (err != OK) {
         if (err == INVALID_OPERATION) {
             jniThrowException(
@@ -1243,7 +1243,7 @@
     }
 }
 
-static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz)
+static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz, jlong srcId)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -1251,7 +1251,7 @@
         return;
     }
 
-    status_t err = mp->releaseDrm();
+    status_t err = mp->releaseDrm(srcId);
     if (err != OK) {
         if (err == INVALID_OPERATION) {
             jniThrowException(
@@ -1425,8 +1425,8 @@
     {"native_setAuxEffectSendLevel", "(F)V",                    (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
     {"native_attachAuxEffect", "(I)V",                          (void *)android_media_MediaPlayer2_attachAuxEffect},
     // Modular DRM
-    { "native_prepareDrm", "([B[B)V",                           (void *)android_media_MediaPlayer2_prepareDrm },
-    { "native_releaseDrm", "()V",                               (void *)android_media_MediaPlayer2_releaseDrm },
+    { "native_prepareDrm", "(J[B[B)V",                          (void *)android_media_MediaPlayer2_prepareDrm },
+    { "native_releaseDrm", "(J)V",                              (void *)android_media_MediaPlayer2_releaseDrm },
 
     // AudioRouting
     {"native_setPreferredDevice", "(Landroid/media/AudioDeviceInfo;)Z", (void *)android_media_MediaPlayer2_setPreferredDevice},
diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp
index d8c5ac9..96c9c1c 100644
--- a/native/webview/plat_support/Android.bp
+++ b/native/webview/plat_support/Android.bp
@@ -23,6 +23,8 @@
 
     srcs: [
         "draw_gl_functor.cpp",
+        "draw_vk_functor.cpp",
+        "functor_utils.cpp",
         "jni_entry_point.cpp",
         "graphics_utils.cpp",
         "graphic_buffer_impl.cpp",
@@ -36,6 +38,7 @@
         "liblog",
         "libui",
         "libutils",
+        "libvulkan",
     ],
 
     // To remove warnings from skia header files
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index e3e52b1..be36b67 100644
--- a/native/webview/plat_support/draw_gl_functor.cpp
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -21,15 +21,13 @@
 
 #include "draw_gl.h"
 
-#include <errno.h>
 #include <jni.h>
 #include <private/hwui/DrawGlInfo.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/time.h>
 #include <utils/Functor.h>
 #include <utils/Log.h>
 
+#include "functor_utils.h"
+
 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
 #define COMPILE_ASSERT(expr, err) \
 __unused static const char (err)[(expr) ? 1 : -1] = "";
@@ -98,27 +96,6 @@
   intptr_t view_context_;
 };
 
-// Raise the file handle soft limit to the hard limit since gralloc buffers
-// uses file handles.
-void RaiseFileNumberLimit() {
-  static bool have_raised_limit = false;
-  if (have_raised_limit)
-    return;
-
-  have_raised_limit = true;
-  struct rlimit limit_struct;
-  limit_struct.rlim_cur = 0;
-  limit_struct.rlim_max = 0;
-  if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) {
-    limit_struct.rlim_cur = limit_struct.rlim_max;
-    if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) {
-      ALOGE("setrlimit failed: %s", strerror(errno));
-    }
-  } else {
-    ALOGE("getrlimit failed: %s", strerror(errno));
-  }
-}
-
 jlong CreateGLFunctor(JNIEnv*, jclass, jlong view_context) {
   RaiseFileNumberLimit();
   return reinterpret_cast<jlong>(new DrawGLFunctor(view_context));
diff --git a/native/webview/plat_support/draw_vk.h b/native/webview/plat_support/draw_vk.h
new file mode 100644
index 0000000..6b7d8d0
--- /dev/null
+++ b/native/webview/plat_support/draw_vk.h
@@ -0,0 +1,125 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+//******************************************************************************
+// This is a copy of the coresponding android_webview/public/browser header.
+// Any changes to the interface should be made there.
+//
+// The purpose of having the copy is twofold:
+//  - it removes the need to have Chromium sources present in the tree in order
+//    to build the plat_support library,
+//  - it captures API that the corresponding Android release supports.
+//******************************************************************************
+
+#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
+#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
+
+#include <vulkan/vulkan.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const int kAwDrawVKInfoVersion = 1;
+
+// Holds the information required to trigger initialization of the Vulkan
+// functor.
+struct InitParams {
+  // All params are input
+  VkInstance instance;
+  VkPhysicalDevice physical_device;
+  VkDevice device;
+  VkQueue queue;
+  uint32_t graphics_queue_index;
+  uint32_t instance_version;
+  const char* const* enabled_extension_names;
+  // Only one of device_features and device_features_2 should be non-null.
+  // If both are null then no features are enabled.
+  VkPhysicalDeviceFeatures* device_features;
+  VkPhysicalDeviceFeatures2* device_features_2;
+};
+
+// Holds the information required to trigger an Vulkan composite operation.
+struct CompositeParams {
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
+
+  // Input: is the render target a FBO
+  bool is_layer;
+
+  // Input: current transform matrix
+  float transform[16];
+
+  // Input WebView should do its main compositing draws into this. It cannot do
+  // anything that would require stopping the render pass.
+  VkCommandBuffer secondary_command_buffer;
+
+  // Input: The main color attachment index where secondary_command_buffer will
+  // eventually be submitted.
+  uint32_t color_attachment_index;
+
+  // Input: A render pass which will be compatible to the one which the
+  // secondary_command_buffer will be submitted into.
+  VkRenderPass compatible_render_pass;
+
+  // Input: Format of the destination surface.
+  VkFormat format;
+
+  // Input: Color space transfer params
+  float G;
+  float A;
+  float B;
+  float C;
+  float D;
+  float E;
+  float F;
+
+  // Input: Color space transformation from linear RGB to D50-adapted XYZ
+  float matrix[9];
+
+  // Input: current clip rect
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+};
+
+// Holds the information for the post-submission callback of main composite
+// draw.
+struct PostCompositeParams {
+  // Input: Fence for the composite command buffer to signal it has finished its
+  // work on the GPU.
+  int fd;
+};
+
+// Holds the information required to trigger an Vulkan operation.
+struct AwDrawVKInfo {
+  int version;  // The AwDrawVKInfo this struct was built with.
+
+  // Input: tells the draw function what action to perform.
+  enum Mode {
+    kModeInit = 0,
+    kModeReInit = 1,
+    kModePreComposite = 2,
+    kModeComposite = 3,
+    kModePostComposite = 4,
+    kModeSync = 5,
+  } mode;
+
+  // Input: The parameters for the functor being called
+  union ParamUnion {
+    struct InitParams init_params;
+    struct CompositeParams composite_params;
+    struct PostCompositeParams post_composite_params;
+  } info;
+};
+
+typedef void(AwDrawVKFunction)(long view_context, AwDrawVKInfo* draw_info);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_VK_H_
diff --git a/native/webview/plat_support/draw_vk_functor.cpp b/native/webview/plat_support/draw_vk_functor.cpp
new file mode 100644
index 0000000..1ba559d
--- /dev/null
+++ b/native/webview/plat_support/draw_vk_functor.cpp
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+// Provides a webviewchromium glue layer adapter from the internal Android
+// Vulkan Functor data types into the types the chromium stack expects, and
+// back.
+
+#define LOG_TAG "webviewchromium_plat_support"
+
+#include "draw_vk.h"
+
+#include <jni.h>
+#include <private/hwui/DrawVkInfo.h>
+#include <utils/Functor.h>
+#include <utils/Log.h>
+
+#include "functor_utils.h"
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+namespace android {
+namespace {
+
+AwDrawVKFunction* g_aw_drawvk_function = NULL;
+
+class DrawVKFunctor : public Functor {
+ public:
+  explicit DrawVKFunctor(jlong view_context) : view_context_(view_context) {}
+  ~DrawVKFunctor() override {}
+
+  // Functor
+  status_t operator ()(int what, void* data) override {
+    using uirenderer::DrawVkInfo;
+    if (!g_aw_drawvk_function) {
+      ALOGE("Cannot draw: no DrawVK Function installed");
+      return DrawVkInfo::kStatusDone;
+    }
+
+    AwDrawVKInfo aw_info;
+    aw_info.version = kAwDrawVKInfoVersion;
+    switch (what) {
+      case DrawVkInfo::kModeComposite: {
+        aw_info.mode = AwDrawVKInfo::kModeComposite;
+        DrawVkInfo* vk_info = reinterpret_cast<DrawVkInfo*>(data);
+
+        // Map across the input values.
+        CompositeParams& params = aw_info.info.composite_params;
+        params.width = vk_info->width;
+        params.height = vk_info->height;
+        params.is_layer = vk_info->isLayer;
+        for (size_t i = 0; i < 16; i++) {
+            params.transform[i] = vk_info->transform[i];
+        }
+        params.secondary_command_buffer = vk_info->secondaryCommandBuffer;
+        params.color_attachment_index = vk_info->colorAttachmentIndex;
+        params.compatible_render_pass = vk_info->compatibleRenderPass;
+        params.format = vk_info->format;
+        params.G = vk_info->G;
+        params.A = vk_info->A;
+        params.B = vk_info->B;
+        params.C = vk_info->C;
+        params.D = vk_info->D;
+        params.E = vk_info->E;
+        params.F = vk_info->F;
+        for (size_t i = 0; i < 9; i++) {
+            params.matrix[i] = vk_info->matrix[i];
+        }
+        params.clip_left = vk_info->clipLeft;
+        params.clip_top = vk_info->clipTop;
+        params.clip_right = vk_info->clipRight;
+        params.clip_bottom = vk_info->clipBottom;
+
+        break;
+      }
+      case DrawVkInfo::kModePostComposite:
+        break;
+      case DrawVkInfo::kModeSync:
+        aw_info.mode = AwDrawVKInfo::kModeSync;
+        break;
+      default:
+        ALOGE("Unexpected DrawVKInfo type %d", what);
+        return DrawVkInfo::kStatusDone;
+    }
+
+    // Invoke the DrawVK method.
+    g_aw_drawvk_function(view_context_, &aw_info);
+
+    return DrawVkInfo::kStatusDone;
+  }
+
+ private:
+  intptr_t view_context_;
+};
+
+jlong CreateVKFunctor(JNIEnv*, jclass, jlong view_context) {
+  RaiseFileNumberLimit();
+  return reinterpret_cast<jlong>(new DrawVKFunctor(view_context));
+}
+
+void DestroyVKFunctor(JNIEnv*, jclass, jlong functor) {
+  delete reinterpret_cast<DrawVKFunctor*>(functor);
+}
+
+void SetChromiumAwDrawVKFunction(JNIEnv*, jclass, jlong draw_function) {
+  g_aw_drawvk_function = reinterpret_cast<AwDrawVKFunction*>(draw_function);
+}
+
+const char kClassName[] = "com/android/webview/chromium/DrawVKFunctor";
+const JNINativeMethod kJniMethods[] = {
+    { "nativeCreateVKFunctor", "(J)J",
+        reinterpret_cast<void*>(CreateVKFunctor) },
+    { "nativeDestroyVKFunctor", "(J)V",
+        reinterpret_cast<void*>(DestroyVKFunctor) },
+    { "nativeSetChromiumAwDrawVKFunction", "(J)V",
+        reinterpret_cast<void*>(SetChromiumAwDrawVKFunction) },
+};
+
+}  // namespace
+
+void RegisterDrawVKFunctor(JNIEnv* env) {
+  jclass clazz = env->FindClass(kClassName);
+  LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
+
+  int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
+  LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
+}
+
+}  // namespace android
diff --git a/native/webview/plat_support/functor_utils.cpp b/native/webview/plat_support/functor_utils.cpp
new file mode 100644
index 0000000..235762d
--- /dev/null
+++ b/native/webview/plat_support/functor_utils.cpp
@@ -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.
+ */
+
+#include "functor_utils.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <utils/Log.h>
+
+namespace android {
+
+void RaiseFileNumberLimit() {
+  static bool have_raised_limit = false;
+  if (have_raised_limit)
+    return;
+
+  have_raised_limit = true;
+  struct rlimit limit_struct;
+  limit_struct.rlim_cur = 0;
+  limit_struct.rlim_max = 0;
+  if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) {
+    limit_struct.rlim_cur = limit_struct.rlim_max;
+    if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) {
+      ALOGE("setrlimit failed: %s", strerror(errno));
+    }
+  } else {
+    ALOGE("getrlimit failed: %s", strerror(errno));
+  }
+}
+
+}  // namespace android
diff --git a/native/webview/plat_support/functor_utils.h b/native/webview/plat_support/functor_utils.h
new file mode 100644
index 0000000..76c0bb6
--- /dev/null
+++ b/native/webview/plat_support/functor_utils.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace android {
+
+// Raise the file handle soft limit to the hard limit since gralloc buffers
+// uses file handles.
+void RaiseFileNumberLimit();
+
+}  // namespace android
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_out.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_out.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
index ed637a7..6f12338 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
index 227c981..9f8c12e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
index 5901ff4..adc1f72 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
index 41cbe4b..dec5c05 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
index 341e7e0..986a9cb 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
index 6ae7413..80b38b3 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
index 06ac9e3..721376c 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
index 4baefb8..246099e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
index 2d0deb9..9a1c642 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
similarity index 95%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
index 3315220..1414b66 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/drawable/car_add_circle_round.xml b/packages/CarSystemUI/res/drawable/car_add_circle_round.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_add_circle_round.xml
rename to packages/CarSystemUI/res/drawable/car_add_circle_round.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_add_white.xml b/packages/CarSystemUI/res/drawable/car_ic_add_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_add_white.xml
rename to packages/CarSystemUI/res/drawable/car_ic_add_white.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
similarity index 94%
rename from packages/SystemUI/res/drawable/car_ic_arrow.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow.xml
index d400ed8..cfacbf9 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
similarity index 93%
rename from packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
index 33a512e..81e7262 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2015 The Android Open Source Project
+  ~ 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_hvac.xml
rename to packages/CarSystemUI/res/drawable/car_ic_hvac.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml b/packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
rename to packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
diff --git a/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
new file mode 100644
index 0000000..eb501e5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
@@ -0,0 +1,27 @@
+<?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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners
+        android:bottomLeftRadius="@dimen/car_radius_3"
+        android:topLeftRadius="0dp"
+        android:bottomRightRadius="@dimen/car_radius_3"
+        android:topRightRadius="0dp"
+        />
+</shape>
diff --git a/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
new file mode 100644
index 0000000..34578fe
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="17dp"
+        android:height="17dp"
+        android:viewportWidth="18.0"
+        android:viewportHeight="18.0">
+    <group
+        android:translateY="0.5"
+        android:translateX="0.5" >
+        <path
+            android:pathData="M9.57,8.5l2.79,-2.78c0.3,-0.3 0.3,-0.8 0,-1.1L9.04,1.29L9.02,1.27C8.7,0.98 8.21,1 7.91,1.31C7.78,1.45 7.71,1.64 7.71,1.84v4.79L4.69,3.61c-0.3,-0.3 -0.79,-0.3 -1.09,0s-0.3,0.79 0,1.09L7.39,8.5L3.6,12.29c-0.3,0.3 -0.3,0.79 0,1.09s0.79,0.3 1.09,0l3.01,-3.01v4.8c0,0.42 0.35,0.77 0.77,0.77c0.19,0 0.39,-0.07 0.53,-0.21l0.04,-0.04l3.32,-3.32c0.3,-0.3 0.3,-0.8 0,-1.1L9.57,8.5zM9.19,6.77v-3.2l1.6,1.6L9.19,6.77zM9.19,13.42v-3.2l1.6,1.6L9.19,13.42zM4.03,9.29c-0.44,0.44 -1.15,0.44 -1.58,0C2.02,8.86 2.02,8.16 2.45,7.72l0.01,-0.01C2.89,7.27 3.59,7.27 4.02,7.7l0.01,0.01C4.47,8.15 4.47,8.85 4.03,9.29zM14.44,7.71c0.44,0.44 0.44,1.15 0,1.58c-0.44,0.44 -1.15,0.44 -1.58,0c-0.44,-0.43 -0.44,-1.13 -0.01,-1.57l0.01,-0.01C13.3,7.28 14,7.27 14.43,7.7C14.44,7.7 14.44,7.71 14.44,7.71z"
+            android:fillColor="#FFFFFF"/>
+    </group>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
index f5a91b5..e1e389d 100644
--- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -1,3 +1,19 @@
+<?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
+  -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="48dp"
         android:height="48dp"
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml
similarity index 97%
rename from packages/SystemUI/res/layout/car_facet_button.xml
rename to packages/CarSystemUI/res/layout/car_facet_button.xml
index ad86049..8e7ebad 100644
--- a/packages/SystemUI/res/layout/car_facet_button.xml
+++ b/packages/CarSystemUI/res/layout/car_facet_button.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2017, The Android Open Source Project
+** 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.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index ee8d357..1d67286 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     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.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index c9f5148..6cd70d6 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_left_navigation_bar.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2016, The Android Open Source Project
+** 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
similarity index 95%
rename from packages/SystemUI/res/layout/car_navigation_button.xml
rename to packages/CarSystemUI/res/layout/car_navigation_button.xml
index 4062eb8..6d8cca9 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2016, The Android Open Source Project
+** 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.
diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_qs_footer.xml
rename to packages/CarSystemUI/res/layout/car_qs_footer.xml
index 3afd4ea..6f19cfc 100644
--- a/packages/SystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_qs_panel.xml
rename to packages/CarSystemUI/res/layout/car_qs_panel.xml
index e7413de..dfa48c3 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
similarity index 98%
copy from packages/SystemUI/res/layout/car_left_navigation_bar.xml
copy to packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2016, The Android Open Source Project
+** 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.
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/colors_car.xml
rename to packages/CarSystemUI/res/values/colors_car.xml
index 49bfb25..2f720f5 100644
--- a/packages/SystemUI/res/values/colors_car.xml
+++ b/packages/CarSystemUI/res/values/colors_car.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
- * Copyright 2017, The Android Open Source Project
+ * 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.
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml
similarity index 97%
rename from packages/SystemUI/res/values/dimens_car.xml
rename to packages/CarSystemUI/res/values/dimens_car.xml
index afbe176..c027f81 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/CarSystemUI/res/values/dimens_car.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
- * Copyright (c) 2016, The Android Open Source Project
+ * 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.
diff --git a/packages/SystemUI/res/values/ids_car.xml b/packages/CarSystemUI/res/values/ids_car.xml
similarity index 100%
rename from packages/SystemUI/res/values/ids_car.xml
rename to packages/CarSystemUI/res/values/ids_car.xml
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/integers_car.xml
rename to packages/CarSystemUI/res/values/integers_car.xml
index fc3623c..472c957 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (c) 2017, The Android Open Source Project
+  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.
diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml
similarity index 96%
rename from packages/SystemUI/res/values/strings_car.xml
rename to packages/CarSystemUI/res/values/strings_car.xml
index 2890cf2..83e91c5 100644
--- a/packages/SystemUI/res/values/strings_car.xml
+++ b/packages/CarSystemUI/res/values/strings_car.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /**
- * Copyright (c) 2016, The Android Open Source Project
+ * 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.
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index dfe5704..f57f26d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -28,6 +28,8 @@
 import com.android.systemui.statusbar.car.hvac.HvacController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.volume.CarVolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
 
 /**
  * Class factory to provide car specific SystemUI components.
@@ -39,6 +41,10 @@
         return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
     }
 
+    public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+        return new CarVolumeDialogComponent(systemUi, context);
+    }
+
     @Override
     public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
         Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
rename to packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 50fefe9..0563418 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,8 +11,9 @@
  * 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
+ * limitations under the License.
  */
+
 package com.android.systemui.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 0389030..b74f199 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -1,16 +1,19 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
 package com.android.systemui.qs.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
index 3e82c54..41c37d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -1,16 +1,19 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
 package com.android.systemui.qs.car;
 
 import android.animation.Animator;
@@ -223,7 +226,7 @@
 
     private void animateHeightChange(boolean opening) {
         // Animation in progress; cancel it to avoid contention.
-        if (mAnimatorSet != null){
+        if (mAnimatorSet != null) {
             mAnimatorSet.cancel();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index 083a747..d5dd3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -1,16 +1,19 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
 package com.android.systemui.qs.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index fc39648..58f80a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -58,28 +58,31 @@
     private final Context mContext;
 
     private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
-    private BluetoothHeadsetClient mBluetoothHeadsetClient;
-
     private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+    private BluetoothHeadsetClient mBluetoothHeadsetClient;
+    private final ServiceListener mHfpServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+            }
+        }
 
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = null;
+            }
+        }
+    };
     private int mLevel;
-
-    /**
-     * An interface indicating the container of a View that will display what the information
-     * in the {@link CarBatteryController}.
-     */
-    public interface BatteryViewHandler {
-        void hideBatteryView();
-        void showBatteryView();
-    }
-
     private BatteryViewHandler mBatteryViewHandler;
 
     public CarBatteryController(Context context) {
         mContext = context;
 
         if (mAdapter == null) {
-           return;
+            return;
         }
 
         mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -159,7 +162,7 @@
 
             }
             BluetoothDevice device =
-                    (BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
+                    (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
             updateBatteryIcon(device, newState);
         }
     }
@@ -261,20 +264,14 @@
         }
     }
 
-    private final ServiceListener mHfpServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
-            }
-        }
+    /**
+     * An interface indicating the container of a View that will display what the information
+     * in the {@link CarBatteryController}.
+     */
+    public interface BatteryViewHandler {
+        void hideBatteryView();
 
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = null;
-            }
-        }
-    };
+        void showBatteryView();
+    }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 20986ad..56db242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -1,3 +1,19 @@
+/*
+ * 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.systemui.statusbar.car;
 
 import android.app.ActivityManager;
@@ -34,7 +50,6 @@
      * Add facet button to this controller. The expected use is for the facet button
      * to get a reference to this controller via {@link com.android.systemui.Dependency}
      * and self add.
-     * @param facetButton
      */
     public void addFacetButton(CarFacetButton facetButton) {
         String[] categories = facetButton.getCategories();
@@ -70,15 +85,16 @@
      * They will then be compared with the supplied StackInfo list.
      * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
      * for consideration if it has the same displayId as the CarFacetButtons.
-     * @param taskInfo of the currently running application
+     *
+     * @param stackInfoList of the currently running application
      */
     public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
         int displayId = getDisplayId();
         ActivityManager.StackInfo validStackInfo = null;
-        for (ActivityManager.StackInfo stackInfo :stackInfoList) {
+        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
             // If the display id is unknown or it matches the stack, it's valid for use
-            if ((displayId == -1 || displayId == stackInfo.displayId) &&
-                    stackInfo.topActivity != null) {
+            if ((displayId == -1 || displayId == stackInfo.displayId)
+                    && stackInfo.topActivity != null) {
                 validStackInfo = stackInfo;
                 break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 084c136..e640baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,3 +1,19 @@
+/*
+ * 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.systemui.statusbar.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4bff5ba..2d90f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -35,6 +35,8 @@
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.StatusBarState;
@@ -252,6 +254,11 @@
         addTemperatureViewToController(mStatusBarWindow);
     }
 
+    @Override
+    protected QS createDefaultQSFragment() {
+        return new CarQSFragment();
+    }
+
     private BatteryController createBatteryController() {
         mCarBatteryController = new CarBatteryController(mContext);
         mCarBatteryController.addBatteryViewHandler(this);
@@ -549,7 +556,7 @@
      */
     public void dismissKeyguard() {
         executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
-            true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+                true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
index d0f0629..8c6b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
@@ -1,3 +1,19 @@
+/*
+ * 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.systemui.statusbar.car;
 
 import android.content.Context;
@@ -16,13 +32,13 @@
             ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils) {
         super(context, callback, lockPatternUtils);
-        mShouldHideNavBar =context.getResources()
+        mShouldHideNavBar = context.getResources()
                 .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
     }
 
     @Override
     protected void updateNavigationBarVisibility(boolean navBarVisible) {
-        if(!mShouldHideNavBar) {
+        if (!mShouldHideNavBar) {
             return;
         }
         CarStatusBar statusBar = (CarStatusBar) mStatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index f2923f7..3288927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -1,3 +1,19 @@
+/*
+ * 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.systemui.statusbar.car;
 
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
@@ -29,7 +45,7 @@
  */
 public class ConnectedDeviceSignalController extends BroadcastReceiver implements
         BluetoothController.Callback {
-    private final static String TAG = "DeviceSignalCtlr";
+    private static final String TAG = "DeviceSignalCtlr";
 
     /**
      * The value that indicates if a network is unavailable. This value is according ot the
@@ -70,6 +86,21 @@
     private final SignalDrawable mSignalDrawable;
 
     private BluetoothHeadsetClient mBluetoothHeadsetClient;
+    private final ServiceListener mHfpServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = null;
+            }
+        }
+    };
 
     public ConnectedDeviceSignalController(Context context, View signalsView) {
         mContext = context;
@@ -87,7 +118,7 @@
                 new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor));
 
         if (mAdapter == null) {
-          return;
+            return;
         }
 
         mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -236,20 +267,4 @@
             mSignalsView.setVisibility(View.GONE);
         }
     }
-
-    private final ServiceListener mHfpServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = null;
-            }
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 47941bf..730c3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -124,4 +124,4 @@
             Log.d(TAG, message);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 2ebf5eb..23fe594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar.car;
@@ -44,7 +44,7 @@
         // Initialize user grid.
         mUserGridView = container.findViewById(R.id.user_grid);
         GridLayoutManager layoutManager = new GridLayoutManager(context,
-            context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+                context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
         mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
         mUserGridView.buildAdapter();
         mUserGridView.setUserSelectionListener(this::onUserSelected);
@@ -54,7 +54,7 @@
         hide();
 
         mShortAnimDuration = container.getResources()
-            .getInteger(android.R.integer.config_shortAnimTime);
+                .getInteger(android.R.integer.config_shortAnimTime);
     }
 
     /**
@@ -108,4 +108,4 @@
                 });
 
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index d802ed8..fb2b57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar.car;
@@ -210,7 +210,7 @@
         public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
             UserRecord userRecord = mUsers.get(position);
             RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
-                getUserRecordIcon(userRecord));
+                    getUserRecordIcon(userRecord));
             circleIcon.setCircular(true);
             holder.mUserAvatarImageView.setImageDrawable(circleIcon);
             holder.mUserNameTextView.setText(userRecord.mInfo.name);
@@ -254,13 +254,13 @@
 
         private void showMaxUserLimitReachedDialog() {
             AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
-                .setTitle(R.string.user_limit_reached_title)
-                .setMessage(getResources().getQuantityString(
-                    R.plurals.user_limit_reached_message,
-                    mCarUserManagerHelper.getMaxSupportedRealUsers(),
-                    mCarUserManagerHelper.getMaxSupportedRealUsers()))
-                .setPositiveButton(android.R.string.ok, null)
-                .create();
+                    .setTitle(R.string.user_limit_reached_title)
+                    .setMessage(getResources().getQuantityString(
+                            R.plurals.user_limit_reached_message,
+                            mCarUserManagerHelper.getMaxSupportedRealUsers(),
+                            mCarUserManagerHelper.getMaxSupportedRealUsers()))
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
             // Sets window flags for the SysUI dialog
             SystemUIDialog.applyFlags(maxUsersDialog);
             maxUsersDialog.show();
@@ -268,17 +268,17 @@
 
         private void showConfirmAddUserDialog() {
             String message = mRes.getString(R.string.user_add_user_message_setup)
-                .concat(System.getProperty("line.separator"))
-                .concat(System.getProperty("line.separator"))
-                .concat(mRes.getString(R.string.user_add_user_message_update));
+                    .concat(System.getProperty("line.separator"))
+                    .concat(System.getProperty("line.separator"))
+                    .concat(mRes.getString(R.string.user_add_user_message_update));
 
             AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
-                .setTitle(R.string.user_add_user_title)
-                .setMessage(message)
-                .setNegativeButton(android.R.string.cancel, this)
-                .setPositiveButton(android.R.string.ok, this)
-                .setOnCancelListener(this)
-                .create();
+                    .setTitle(R.string.user_add_user_title)
+                    .setMessage(message)
+                    .setNegativeButton(android.R.string.cancel, this)
+                    .setPositiveButton(android.R.string.ok, this)
+                    .setOnCancelListener(this)
+                    .create();
             // Sets window flags for the SysUI dialog
             SystemUIDialog.applyFlags(addUserDialog);
             addUserDialog.show();
@@ -298,7 +298,7 @@
 
             if (userRecord.mIsAddUser) {
                 return UserIcons.convertToBitmap(mContext
-                    .getDrawable(R.drawable.car_add_circle_round));
+                        .getDrawable(R.drawable.car_add_circle_round));
             }
 
             return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 6c924e3..aec31ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -41,117 +41,13 @@
 public class HvacController {
 
     public static final String TAG = "HvacController";
-    public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
+    public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
 
     private Context mContext;
     private Handler mHandler;
     private Car mCar;
     private CarHvacManager mHvacManager;
     private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
-
-    public HvacController(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Create connection to the Car service. Note: call backs from the Car service
-     * ({@link CarHvacManager}) will happen on the same thread this method was called from.
-     */
-    public void connectToCarService() {
-        mHandler = new Handler();
-        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
-        if (mCar != null) {
-            // note: this connect call handles the retries
-            mCar.connect();
-        }
-    }
-
-    /**
-     * Registers callbacks and initializes components upon connection.
-     */
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                service.linkToDeath(mRestart, 0);
-                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
-                mHvacManager.registerCallback(mHardwareCallback);
-                initComponents();
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to correctly connect to HVAC", e);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            destroyHvacManager();
-        }
-    };
-
-    private void destroyHvacManager() {
-        if (mHvacManager != null) {
-            mHvacManager.unregisterCallback(mHardwareCallback);
-            mHvacManager = null;
-        }
-    }
-
-    /**
-     * If the connection to car service goes away then restart it.
-     */
-    private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            Log.d(TAG, "Death of HVAC triggering a restart");
-            if (mCar != null) {
-                mCar.disconnect();
-            }
-            destroyHvacManager();
-            mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
-        }
-    };
-
-    /**
-     * Add component to list and initialize it if the connection is up.
-     * @param temperatureView
-     */
-    public void addHvacTextView(TemperatureView temperatureView) {
-
-        HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
-        if (!mTempComponents.containsKey(hvacKey)) {
-            mTempComponents.put(hvacKey, new ArrayList<>());
-        }
-        mTempComponents.get(hvacKey).add(temperatureView);
-        initComponent(temperatureView);
-    }
-
-    private void initComponents() {
-        Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
-                mTempComponents.entrySet().iterator();
-        while (iterator.hasNext()) {
-            Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
-            List<TemperatureView> temperatureViews = next.getValue();
-            for (TemperatureView view : temperatureViews) {
-                initComponent(view);
-            }
-        }
-    }
-
-
-    private void initComponent(TemperatureView view) {
-        int id = view.getPropertyId();
-        int zone = view.getAreaId();
-        try {
-            if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
-                view.setTemp(Float.NaN);
-                return;
-            }
-            view.setTemp(mHvacManager.getFloatProperty(id, zone));
-        } catch (Exception e) {
-            view.setTemp(Float.NaN);
-            Log.e(TAG, "Failed to get value from hvac service", e);
-        }
-    }
-
     /**
      * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
      * match.
@@ -179,10 +75,109 @@
 
         @Override
         public void onErrorEvent(final int propertyId, final int zone) {
-            Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
-                    " zone: " + zone);
+            Log.d(TAG, "HVAC error event, propertyId: " + propertyId
+                    + " zone: " + zone);
         }
     };
+    /**
+     * If the connection to car service goes away then restart it.
+     */
+    private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.d(TAG, "Death of HVAC triggering a restart");
+            if (mCar != null) {
+                mCar.disconnect();
+            }
+            destroyHvacManager();
+            mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
+        }
+    };
+    /**
+     * Registers callbacks and initializes components upon connection.
+     */
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                service.linkToDeath(mRestart, 0);
+                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+                mHvacManager.registerCallback(mHardwareCallback);
+                initComponents();
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to correctly connect to HVAC", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            destroyHvacManager();
+        }
+    };
+
+    public HvacController(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Create connection to the Car service. Note: call backs from the Car service
+     * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+     */
+    public void connectToCarService() {
+        mHandler = new Handler();
+        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
+        if (mCar != null) {
+            // note: this connect call handles the retries
+            mCar.connect();
+        }
+    }
+
+    private void destroyHvacManager() {
+        if (mHvacManager != null) {
+            mHvacManager.unregisterCallback(mHardwareCallback);
+            mHvacManager = null;
+        }
+    }
+
+    /**
+     * Add component to list and initialize it if the connection is up.
+     */
+    public void addHvacTextView(TemperatureView temperatureView) {
+
+        HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
+        if (!mTempComponents.containsKey(hvacKey)) {
+            mTempComponents.put(hvacKey, new ArrayList<>());
+        }
+        mTempComponents.get(hvacKey).add(temperatureView);
+        initComponent(temperatureView);
+    }
+
+    private void initComponents() {
+        Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
+                mTempComponents.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+            List<TemperatureView> temperatureViews = next.getValue();
+            for (TemperatureView view : temperatureViews) {
+                initComponent(view);
+            }
+        }
+    }
+
+    private void initComponent(TemperatureView view) {
+        int id = view.getPropertyId();
+        int zone = view.getAreaId();
+        try {
+            if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+                view.setTemp(Float.NaN);
+                return;
+            }
+            view.setTemp(mHvacManager.getFloatProperty(id, zone));
+        } catch (Exception e) {
+            view.setTemp(Float.NaN);
+            Log.e(TAG, "Failed to get value from hvac service", e);
+        }
+    }
 
     /**
      * Removes all registered components. This is useful if you need to rebuild the UI since
@@ -200,7 +195,7 @@
         int mPropertyId;
         int mAreaId;
 
-        public HvacKey(int propertyId, int areaId) {
+        private HvacKey(int propertyId, int areaId) {
             mPropertyId = propertyId;
             mAreaId = areaId;
         }
@@ -210,8 +205,8 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             HvacKey hvacKey = (HvacKey) o;
-            return mPropertyId == hvacKey.mPropertyId &&
-                    mAreaId == hvacKey.mAreaId;
+            return mPropertyId == hvacKey.mPropertyId
+                    && mAreaId == hvacKey.mAreaId;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 4d8ce43..507c60f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -40,7 +40,7 @@
     public TemperatureTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
-        mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
+        mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
         mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
         String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
         mTempFormat = (format == null) ? "%.1f\u00B0" : format;
@@ -48,6 +48,7 @@
 
     /**
      * Formats the float for display
+     *
      * @param temp - The current temp or NaN
      */
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
new file mode 100644
index 0000000..71cc19b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -0,0 +1,36 @@
+/*
+ * 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.systemui.volume;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.VolumeDialog;
+
+/**
+ * Allows for adding car specific dialog when the volume dialog is created.
+ */
+public class CarVolumeDialogComponent extends VolumeDialogComponent {
+
+    public CarVolumeDialogComponent(SystemUI sysui, Context context) {
+        super(sysui, context);
+    }
+
+    protected VolumeDialog createDefault() {
+        return new CarVolumeDialogImpl(mContext);
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
new file mode 100644
index 0000000..12df263
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -0,0 +1,602 @@
+/*
+ * 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.systemui.volume;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.car.media.ICarVolumeCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.ServiceConnection;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemAdapter.BackgroundStyle;
+import androidx.car.widget.ListItemProvider.ListProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SeekbarListItem;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.VolumeDialog;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Car version of the volume dialog.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class CarVolumeDialogImpl implements VolumeDialog {
+
+    private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
+
+    private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
+    private static final String XML_TAG_VOLUME_ITEM = "item";
+    private static final int HOVERING_TIMEOUT = 16000;
+    private static final int NORMAL_TIMEOUT = 3000;
+    private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
+    private static final int DISMISS_DELAY_IN_MILLIS = 50;
+    private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
+
+    private final Context mContext;
+    private final H mHandler = new H();
+    // All the volume items.
+    private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
+    // Available volume items in car audio manager.
+    private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
+    // Volume items in the PagedListView.
+    private final List<ListItem> mVolumeLineItems = new ArrayList<>();
+    private final KeyguardManager mKeyguard;
+    private Window mWindow;
+    private CustomDialog mDialog;
+    private PagedListView mListView;
+    private ListItemAdapter mPagedListAdapter;
+    private Car mCar;
+    private CarAudioManager mCarAudioManager;
+    private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
+        @Override
+        public void onGroupVolumeChanged(int groupId, int flags) {
+            VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+            int value = getSeekbarValue(mCarAudioManager, groupId);
+            // Do not update the progress if it is the same as before. When car audio manager sets
+            // its group volume caused by the seekbar progress changed, it also triggers this
+            // callback. Updating the seekbar at the same time could block the continuous seeking.
+            if (value != volumeItem.progress) {
+                volumeItem.listItem.setProgress(value);
+                volumeItem.progress = value;
+            }
+            if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+                mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onMasterMuteChanged(int flags) {
+            // ignored
+        }
+    };
+    private boolean mHovering;
+    private boolean mShowing;
+    private boolean mExpanded;
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mExpanded = false;
+                mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+                int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+                // Populates volume slider items from volume groups to UI.
+                for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+                    VolumeItem volumeItem = getVolumeItemForUsages(
+                            mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+                    mAvailableVolumeItems.add(volumeItem);
+                    // The first one is the default item.
+                    if (groupId == 0) {
+                        volumeItem.defaultItem = true;
+                        addSeekbarListItem(volumeItem, groupId,
+                                R.drawable.car_ic_keyboard_arrow_down,
+                                new ExpandIconListener());
+                    }
+                }
+
+                // If list is already initiated, update its content.
+                if (mPagedListAdapter != null) {
+                    mPagedListAdapter.notifyDataSetChanged();
+                }
+                mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car is not connected!", e);
+            }
+        }
+
+        /**
+         * This does not get called when service is properly disconnected.
+         * So we need to also handle cleanups in destroy().
+         */
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            cleanupAudioManager();
+        }
+    };
+
+    public CarVolumeDialogImpl(Context context) {
+        mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
+        mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mCar = Car.createCar(mContext, mServiceConnection);
+    }
+
+    private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+        try {
+            return carAudioManager.getGroupVolume(volumeGroupId);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+        }
+        return 0;
+    }
+
+    private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+        try {
+            return carAudioManager.getGroupMaxVolume(volumeGroupId);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Build the volume window and connect to the CarService which registers with car audio
+     * manager.
+     */
+    @Override
+    public void init(int windowType, Callback callback) {
+        initDialog();
+
+        mCar.connect();
+    }
+
+    @Override
+    public void destroy() {
+        mHandler.removeCallbacksAndMessages(null);
+
+        cleanupAudioManager();
+        // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
+        // audio manager beforehand.
+        mCar.disconnect();
+    }
+
+    private void initDialog() {
+        loadAudioUsageItems();
+        mVolumeLineItems.clear();
+        mDialog = new CustomDialog(mContext);
+
+        mHovering = false;
+        mShowing = false;
+        mExpanded = false;
+        mWindow = mDialog.getWindow();
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+        mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+        mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+        mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+        mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+        final WindowManager.LayoutParams lp = mWindow.getAttributes();
+        lp.format = PixelFormat.TRANSLUCENT;
+        lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+        lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+        lp.windowAnimations = -1;
+        mWindow.setAttributes(lp);
+        mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+        mDialog.setCanceledOnTouchOutside(true);
+        mDialog.setContentView(R.layout.car_volume_dialog);
+        mDialog.setOnShowListener(dialog -> {
+            mListView.setTranslationY(-mListView.getHeight());
+            mListView.setAlpha(0);
+            mListView.animate()
+                    .alpha(1)
+                    .translationY(0)
+                    .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+                    .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+                    .start();
+        });
+        mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
+        mListView.setOnHoverListener((v, event) -> {
+            int action = event.getActionMasked();
+            mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+                    || (action == MotionEvent.ACTION_HOVER_MOVE);
+            rescheduleTimeoutH();
+            return true;
+        });
+
+        mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
+                BackgroundStyle.PANEL);
+        mListView.setAdapter(mPagedListAdapter);
+        mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+    }
+
+
+    private void showH(int reason) {
+        if (D.BUG) {
+            Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
+        }
+
+        mHandler.removeMessages(H.SHOW);
+        mHandler.removeMessages(H.DISMISS);
+        rescheduleTimeoutH();
+        // Refresh the data set before showing.
+        mPagedListAdapter.notifyDataSetChanged();
+        if (mShowing) {
+            return;
+        }
+        mShowing = true;
+
+        mDialog.show();
+        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+    }
+
+    private void rescheduleTimeoutH() {
+        mHandler.removeMessages(H.DISMISS);
+        final int timeout = computeTimeoutH();
+        mHandler.sendMessageDelayed(mHandler
+                .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
+
+        if (D.BUG) {
+            Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
+        }
+    }
+
+    private int computeTimeoutH() {
+        return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+    }
+
+    private void dismissH(int reason) {
+        if (D.BUG) {
+            Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
+        }
+
+        mHandler.removeMessages(H.DISMISS);
+        mHandler.removeMessages(H.SHOW);
+        if (!mShowing) {
+            return;
+        }
+
+        mListView.animate().cancel();
+
+        mListView.setTranslationY(0);
+        mListView.setAlpha(1);
+        mListView.animate()
+                .alpha(0)
+                .translationY(-mListView.getHeight())
+                .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+                .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+                .withEndAction(() -> mHandler.postDelayed(() -> {
+                    if (D.BUG) {
+                        Log.d(TAG, "mDialog.dismiss()");
+                    }
+                    mDialog.dismiss();
+                    mShowing = false;
+                }, DISMISS_DELAY_IN_MILLIS))
+                .start();
+
+        Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+    }
+
+    private void loadAudioUsageItems() {
+        try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            int type;
+            // Traverse to the first start tag
+            while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+                    && type != XmlResourceParser.START_TAG) {
+                // Do Nothing (moving parser to start element)
+            }
+
+            if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
+                throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
+            }
+            int outerDepth = parser.getDepth();
+            int rank = 0;
+            while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+                    && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlResourceParser.END_TAG) {
+                    continue;
+                }
+                if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
+                    TypedArray item = mContext.getResources().obtainAttributes(
+                            attrs, R.styleable.carVolumeItems_item);
+                    int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+                    if (usage >= 0) {
+                        VolumeItem volumeItem = new VolumeItem();
+                        volumeItem.rank = rank;
+                        volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
+                                0);
+                        mVolumeItems.put(usage, volumeItem);
+                        rank++;
+                    }
+                    item.recycle();
+                }
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Log.e(TAG, "Error parsing volume groups configuration", e);
+        }
+    }
+
+    private VolumeItem getVolumeItemForUsages(int[] usages) {
+        int rank = Integer.MAX_VALUE;
+        VolumeItem result = null;
+        for (int usage : usages) {
+            VolumeItem volumeItem = mVolumeItems.get(usage);
+            if (volumeItem.rank < rank) {
+                rank = volumeItem.rank;
+                result = volumeItem;
+            }
+        }
+        return result;
+    }
+
+    private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem,
+            int volumeGroupId,
+            int supplementalIconId,
+            @Nullable View.OnClickListener supplementalIconOnClickListener) {
+        SeekbarListItem listItem = new SeekbarListItem(mContext);
+        listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
+        int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
+        int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
+        listItem.setProgress(progress);
+        listItem.setOnSeekBarChangeListener(new CarVolumeDialogImpl
+                .VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
+        Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
+        primaryIcon.mutate().setTint(color);
+        listItem.setPrimaryActionIcon(primaryIcon);
+        if (supplementalIconId != 0) {
+            Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+            supplementalIcon.mutate().setTint(color);
+            listItem.setSupplementalIcon(supplementalIcon, true);
+            listItem.setSupplementalIconListener(supplementalIconOnClickListener);
+        } else {
+            listItem.setSupplementalEmptyIcon(true);
+            listItem.setSupplementalIconListener(null);
+        }
+
+        mVolumeLineItems.add(listItem);
+        volumeItem.listItem = listItem;
+        volumeItem.progress = progress;
+        return listItem;
+    }
+
+    private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
+        for (int i = 0; i < mVolumeItems.size(); ++i) {
+            VolumeItem volumeItem = mVolumeItems.valueAt(i);
+            if (volumeItem.listItem == targetItem) {
+                return volumeItem;
+            }
+        }
+        return null;
+    }
+
+    private void cleanupAudioManager() {
+        try {
+            mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+        }
+        mVolumeLineItems.clear();
+        mCarAudioManager = null;
+    }
+
+    /**
+     * Wrapper class which contains information of each volume group.
+     */
+    private static class VolumeItem {
+
+        private int rank;
+        private boolean defaultItem = false;
+        private @DrawableRes int icon;
+        private SeekbarListItem listItem;
+        private int progress;
+    }
+
+    private final class H extends Handler {
+
+        private static final int SHOW = 1;
+        private static final int DISMISS = 2;
+
+        private H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SHOW:
+                    showH(msg.arg1);
+                    break;
+                case DISMISS:
+                    dismissH(msg.arg1);
+                    break;
+                default:
+            }
+        }
+    }
+
+    private final class CustomDialog extends Dialog implements DialogInterface {
+
+        private CustomDialog(Context context) {
+            super(context, com.android.systemui.R.style.qs_theme);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            rescheduleTimeoutH();
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected void onStart() {
+            super.setCanceledOnTouchOutside(true);
+            super.onStart();
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (isShowing()) {
+                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                    mHandler.obtainMessage(
+                            H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private final class ExpandIconListener implements View.OnClickListener {
+
+        @Override
+        public void onClick(final View v) {
+            mExpanded = !mExpanded;
+            Animator inAnimator;
+            if (mExpanded) {
+                for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+                    // Adding the items which are not coming from the default item.
+                    VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+                    if (volumeItem.defaultItem) {
+                        // Set progress here due to the progress of seekbar may not be updated.
+                        volumeItem.listItem.setProgress(volumeItem.progress);
+                    } else {
+                        addSeekbarListItem(volumeItem, groupId, 0, null);
+                    }
+                }
+                inAnimator = AnimatorInflater.loadAnimator(
+                        mContext, R.anim.car_arrow_fade_in_rotate_up);
+            } else {
+                // Only keeping the default stream if it is not expended.
+                Iterator itr = mVolumeLineItems.iterator();
+                while (itr.hasNext()) {
+                    SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+                    VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+                    if (!volumeItem.defaultItem) {
+                        itr.remove();
+                    } else {
+                        // Set progress here due to the progress of seekbar may not be updated.
+                        seekbarListItem.setProgress(volumeItem.progress);
+                    }
+                }
+                inAnimator = AnimatorInflater.loadAnimator(
+                        mContext, R.anim.car_arrow_fade_in_rotate_down);
+            }
+
+            Animator outAnimator = AnimatorInflater.loadAnimator(
+                    mContext, R.anim.car_arrow_fade_out);
+            inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+            AnimatorSet animators = new AnimatorSet();
+            animators.playTogether(outAnimator, inAnimator);
+            animators.setTarget(v);
+            animators.start();
+            mPagedListAdapter.notifyDataSetChanged();
+        }
+    }
+
+    private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+
+        private final int mVolumeGroupId;
+        private final CarAudioManager mCarAudioManager;
+
+        private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
+            mVolumeGroupId = volumeGroupId;
+            mCarAudioManager = carAudioManager;
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (!fromUser) {
+                // For instance, if this event is originated from AudioService,
+                // we can ignore it as it has already been handled and doesn't need to be
+                // sent back down again.
+                return;
+            }
+            try {
+                if (mCarAudioManager == null) {
+                    Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+                    return;
+                }
+                mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+                mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car is not connected!", e);
+            }
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+        }
+    }
+}
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index 7057ce6..aef6a3c 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -9,4 +9,5 @@
 jminjie@google.com
 satk@google.com
 shuoq@google.com
-refuhoo@google.com
\ No newline at end of file
+refuhoo@google.com
+nazaninb@google.com
\ 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 4b212c2..0cad5af 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -19,8 +19,7 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -241,7 +240,7 @@
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
         if (Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) {
+                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
             if (mNotificationCategorizer.shouldSilence(entry)) {
                 final int importance = entry.getImportance() < IMPORTANCE_LOW
                         ? entry.getImportance() : IMPORTANCE_LOW;
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index c9ee5c8..1eb4b74 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -224,7 +224,7 @@
                 root.flags |= Root.FLAG_REMOVABLE_USB;
             }
 
-            if (!VolumeInfo.ID_EMULATED_INTERNAL.equals(volume.getId())) {
+            if (volume.getType() != VolumeInfo.TYPE_EMULATED) {
                 root.flags |= Root.FLAG_SUPPORTS_EJECT;
             }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 56b768f..df5b146 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1165,6 +1165,12 @@
                 GlobalSettingsProto.SmartSelection.UPDATE_METADATA_URL);
         p.end(smartSelectToken);
 
+        final long smartSuggestionsToken = p.start(GlobalSettingsProto.SMART_SUGGESTIONS);
+        dumpSetting(s, p,
+                Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED,
+                GlobalSettingsProto.SmartSuggestions.SERVICE_EXPLICITLY_ENABLED);
+        p.end(smartSuggestionsToken);
+
         final long smsToken = p.start(GlobalSettingsProto.SMS);
         dumpSetting(s, p,
                 Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
@@ -1306,6 +1312,9 @@
         dumpSetting(s, p,
                 Settings.Global.WARNING_TEMPERATURE,
                 GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL);
+        dumpSetting(s, p,
+                Settings.Global.USB_ALARM_TEMPERATURE,
+                GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL);
         p.end(tempWarningToken);
 
         final long tetherToken = p.start(GlobalSettingsProto.TETHER);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cb55231..83e8369 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -130,6 +130,7 @@
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     <uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
+    <uses-permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0b9b27f..7d53c2f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -223,6 +223,9 @@
 
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
 
+    <!-- Permission to change the display color -->
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/res/drawable/car_ic_music.xml b/packages/SystemUI/res/drawable/car_ic_music.xml
deleted file mode 100644
index f90cd69..0000000
--- a/packages/SystemUI/res/drawable/car_ic_music.xml
+++ /dev/null
@@ -1,30 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="56dp"
-    android:height="56dp"
-    android:viewportWidth="48"
-    android:viewportHeight="48">
-
-  <path
-      android:fillAlpha=".1"
-      android:strokeAlpha=".1"
-      android:pathData="M0 0h48v48H0z" />
-  <path
-      android:fillColor="@color/car_grey_50"
-      android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14
-14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_ic_navigation.xml b/packages/SystemUI/res/drawable/car_ic_navigation.xml
deleted file mode 100644
index 328efa0..0000000
--- a/packages/SystemUI/res/drawable/car_ic_navigation.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="37dp"
-    android:viewportWidth="32.0"
-    android:viewportHeight="37.0">
-  <path
-      android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
-      android:strokeColor="#00000000"
-      android:fillType="evenOdd"
-      android:fillColor="@color/car_grey_50"
-      android:strokeWidth="1"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
deleted file mode 100644
index 61d937b90..0000000
--- a/packages/SystemUI/res/drawable/car_ic_notification.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="56dp"
-    android:height="56dp"
-    android:viewportWidth="48"
-    android:viewportHeight="48">
-
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
-4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
-1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
-9v12z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
deleted file mode 100644
index 4651dcb..0000000
--- a/packages/SystemUI/res/drawable/car_ic_overview.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="56dp"
-    android:height="56dp"
-    android:viewportWidth="48"
-    android:viewportHeight="48">
-
-    <path
-        android:pathData="M0 0h48v48H0z" />
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
-0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
deleted file mode 100644
index 25b449a..0000000
--- a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorBackgroundFloating" />
-    <corners
-        android:bottomLeftRadius="@dimen/car_radius_3"
-        android:topLeftRadius="0dp"
-        android:bottomRightRadius="@dimen/car_radius_3"
-        android:topRightRadius="0dp"
-        />
-</shape>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
deleted file mode 100644
index d568d0d..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:background="@drawable/system_bar_background">
-
-    <LinearLayout
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:orientation="horizontal"
-        android:id="@+id/nav_buttons"
-        android:gravity="left"
-        android:paddingLeft="30dp"
-        android:layout_weight="1"
-        android:animateLayoutChanges="true">
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/home"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            android:src="@drawable/car_ic_overview"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/hvac"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            systemui:broadcast="true"
-            android:src="@drawable/car_ic_hvac"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="right"
-        android:orientation="horizontal">
-
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:src="@drawable/car_ic_notification"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="20dp"
-            android:paddingRight="20dp"
-            android:alpha="0.7"
-        />
-
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_clock_end_padding"
-            android:gravity="center_vertical"
-            android:paddingRight="20dp"
-        />
-
-        <Space
-            android:layout_width="10dp"
-            android:layout_height="match_parent"/>
-
-    </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
deleted file mode 100644
index 4ba6c06..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:background="@drawable/system_bar_background">
-
-    <LinearLayout
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:orientation="horizontal"
-        android:id="@+id/nav_buttons"
-        android:gravity="left"
-        android:paddingLeft="30dp"
-        android:layout_weight="1"
-        android:animateLayoutChanges="true">
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/home"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            android:src="@drawable/car_ic_overview"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/hvac"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            systemui:broadcast="true"
-            android:src="@drawable/car_ic_hvac"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-    </LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
deleted file mode 100644
index 91ba026..0000000
--- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:orientation="vertical"
-    android:background="@drawable/system_bar_background">
-
-    <LinearLayout
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:id="@+id/nav_buttons"
-        android:orientation="vertical"
-        android:gravity="top"
-        android:paddingTop="30dp"
-        android:layout_weight="1"
-        android:background="@drawable/system_bar_background"
-        android:animateLayoutChanges="true">
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/home"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            android:src="@drawable/car_ic_overview"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingTop="30dp"
-            android:paddingBottom="30dp"
-        />
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/hvac"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            systemui:broadcast="true"
-            android:src="@drawable/car_ic_hvac"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingTop="30dp"
-            android:paddingBottom="30dp"
-        />
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="bottom"
-        android:orientation="vertical">
-
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:src="@drawable/car_ic_notification"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingTop="20dp"
-            android:paddingBottom="20dp"
-            android:alpha="0.7"
-        />
-
-
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_clock_end_padding"
-            android:gravity="center_horizontal"
-            android:paddingBottom="20dp"
-        />
-
-        <Space
-            android:layout_height="10dp"
-            android:layout_width="match_parent"/>
-
-    </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
deleted file mode 100644
index f2ef301..0000000
--- a/packages/SystemUI/res/layout/car_status_bar_header.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- Extends LinearLayout -->
-<com.android.systemui.qs.car.CarStatusBarHeader
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/header"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/car_qs_header_system_icons_area_height"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp" >
-
-    <include layout="@layout/system_icons"
-             android:layout_width="wrap_content"
-             android:layout_height="match_parent"
-             android:gravity="center_vertical|end"
-             android:layout_weight="1"
-    />
-
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:singleLine="true"
-        android:paddingStart="@dimen/status_bar_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_clock_end_padding"
-        android:gravity="center_vertical|end"
-    />
-</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
deleted file mode 100644
index e16014b..0000000
--- a/packages/SystemUI/res/layout/car_top_navigation_bar.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
-** 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:background="@drawable/system_bar_background">
-
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:singleLine="true"
-        android:paddingStart="@dimen/status_bar_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_clock_end_padding"
-        android:gravity="center_vertical"
-    />
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
deleted file mode 100644
index a6beaa1..0000000
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ /dev/null
@@ -1,31 +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.
--->
-<androidx.car.widget.PagedListView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:background="@drawable/car_card_rounded_background"
-    android:id="@+id/volume_list"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginStart="@dimen/car_margin"
-    android:layout_marginEnd="@dimen/car_margin"
-    android:minWidth="@dimen/volume_dialog_panel_width"
-    android:theme="@style/Theme.Car.NoActionBar"
-    app:dividerStartMargin="@dimen/car_keyline_1"
-    app:dividerEndMargin="@dimen/car_keyline_1"
-    app:gutter="none"
-    app:showPagedListViewDivider="true"
-    app:scrollBarEnabled="false" />
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 0cc3c9e..34c208a 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -56,11 +56,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
-    <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
-              android:layout="@layout/car_fullscreen_user_switcher"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"/>
-
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
deleted file mode 100644
index 8c760fc..0000000
--- a/packages/SystemUI/res/values/arrays_car.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
-    <!-- These should be overriden in an overlay. The default implementation is empty.
-         There needs to be correspondence per index between these arrays, which means that if there
-         isn't a longpress action associated with a shortcut item, put in an empty item to make
-         sure everything lines up.
-    -->
-    <array name="car_facet_icons" />
-    <array name="car_facet_intent_uris" />
-    <array name="car_facet_longpress_intent_uris" />
-    <array name="car_facet_package_filters"/>
-    <array name="car_facet_category_filters"/>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b0a519c..8e0bfb6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,7 @@
     <dimen name="smart_reply_button_stroke_width">1dp</dimen>
     <dimen name="smart_reply_button_font_size">14sp</dimen>
     <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+    <dimen name="smart_action_button_icon_size">24dp</dimen>
     <dimen name="smart_action_button_icon_padding">10dp</dimen>
 
     <!-- A reasonable upper bound for the height of the smart reply button. The measuring code
diff --git a/packages/SystemUI/res/xml/car_volume_items.xml b/packages/SystemUI/res/xml/car_volume_items.xml
deleted file mode 100644
index 742dfdd..0000000
--- a/packages/SystemUI/res/xml/car_volume_items.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- *
- * 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.
-*/
--->
-
-<!--
-  Defines all possible items on car volume settings UI, keyed by usage.
-
-  This enables the CarSettings UI to associate VolumeGroups surfaced by
-  CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon)
-  for presentation.
-
-  Order matters in this configuration. If one volume group contains multiple
-  audio usages, the first one appears in this file would be picked to be
-  presented on UI.
-
-  When overriding this configuration, please consult also the
-  car_volume_groups.xml, which is read by car audio service.
--->
-<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
-  <item car:usage="unknown"
-        car:icon="@drawable/car_ic_music"/>
-  <item car:usage="media"
-        car:icon="@drawable/car_ic_music"/>
-  <item car:usage="voice_communication"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
-  <item car:usage="voice_communication_signalling"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
-  <item car:usage="alarm"
-        car:icon="@*android:drawable/ic_audio_alarm"/>
-  <item car:usage="notification"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_ringtone"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
-  <item car:usage="notification_communication_request"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_communication_instant"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_communication_delayed"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_event"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="assistance_accessibility"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="assistance_navigation_guidance"
-        car:icon="@drawable/car_ic_navigation"/>
-  <item car:usage="assistance_sonification"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="game"
-        car:icon="@drawable/car_ic_music"/>
-  <item car:usage="assistant"
-        car:icon="@drawable/car_ic_music"/>
-</carVolumeItems>
-
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
index 12699d5..18dc185 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -58,4 +58,8 @@
         encoder.endStream();
         return true;
     }
+
+    public int getDisplayId() {
+        return mWrapped.getDisplayId();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b55aa5c..904f944 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1904,13 +1904,18 @@
                     + slotId + ", state=" + state +")");
         }
 
+        boolean becameAbsent = false;
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.w(TAG, "invalid subId in handleSimStateChange()");
             /* Only handle No SIM(ABSENT) due to handleServiceStateChange() handle other case */
             if (state == State.ABSENT) {
                 updateTelephonyCapable(true);
+                // Even though the subscription is not valid anymore, we need to notify that the
+                // SIM card was removed so we can update the UI.
+                becameAbsent = true;
+            } else {
+                return;
             }
-            return;
         }
 
         SimData data = mSimDatas.get(subId);
@@ -1925,7 +1930,7 @@
             data.subId = subId;
             data.slotId = slotId;
         }
-        if (changed && state != State.UNKNOWN) {
+        if ((changed || becameAbsent) && state != State.UNKNOWN) {
             for (int i = 0; i < mCallbacks.size(); i++) {
                 KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
                 if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 053ea67..38dadd4 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -19,6 +19,8 @@
 import static android.app.StatusBarManager.DISABLE_NONE;
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 
+import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
+
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.animation.ArgbEvaluator;
@@ -88,6 +90,9 @@
     private int mShowPercentMode = MODE_DEFAULT;
     private boolean mForceShowPercent;
     private boolean mShowPercentAvailable;
+    // Some places may need to show the battery conditionally, and not obey the tuner
+    private boolean mIgnoreTunerUpdates;
+    private boolean mIsSubscribedForTunerUpdates;
 
     private int mDarkModeBackgroundColor;
     private int mDarkModeFillColor;
@@ -163,6 +168,7 @@
 
         setClipChildren(false);
         setClipToPadding(false);
+        Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
     }
 
     public void setForceShowPercent(boolean show) {
@@ -183,6 +189,44 @@
     }
 
     /**
+     * Set {@code true} to turn off BatteryMeterView's subscribing to the tuner for updates, and
+     * thus avoid it controlling its own visibility
+     *
+     * @param ignore whether to ignore the tuner or not
+     */
+    public void setIgnoreTunerUpdates(boolean ignore) {
+        mIgnoreTunerUpdates = ignore;
+        updateTunerSubscription();
+    }
+
+    private void updateTunerSubscription() {
+        if (mIgnoreTunerUpdates) {
+            unsubscribeFromTunerUpdates();
+        } else {
+            subscribeForTunerUpdates();
+        }
+    }
+
+    private void subscribeForTunerUpdates() {
+        if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
+            return;
+        }
+
+        Dependency.get(TunerService.class)
+                .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+        mIsSubscribedForTunerUpdates = true;
+    }
+
+    private void unsubscribeFromTunerUpdates() {
+        if (!mIsSubscribedForTunerUpdates) {
+            return;
+        }
+
+        Dependency.get(TunerService.class).removeTunable(this);
+        mIsSubscribedForTunerUpdates = false;
+    }
+
+    /**
      * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
      * revert back to dark-mode-based/tinted colors.
      *
@@ -247,9 +291,7 @@
         getContext().getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
         updateShowPercent();
-        Dependency.get(TunerService.class)
-                .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
-        Dependency.get(ConfigurationController.class).addCallback(this);
+        subscribeForTunerUpdates();
         mUserTracker.startTracking();
     }
 
@@ -259,8 +301,7 @@
         mUserTracker.stopTracking();
         mBatteryController.removeCallback(this);
         getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
-        Dependency.get(TunerService.class).removeTunable(this);
-        Dependency.get(ConfigurationController.class).removeCallback(this);
+        unsubscribeFromTunerUpdates();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 417d516..867c917 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -62,6 +62,7 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.volume.VolumeDialogComponent;
 
 import java.util.function.Consumer;
 
@@ -132,6 +133,10 @@
         return new QSTileHost(context, statusBar, iconController);
     }
 
+    public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+        return new VolumeDialogComponent(systemUi, context);
+    }
+
     public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
             Context context) {
         providers.put(StatusBarStateController.class, StatusBarStateController::new);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index a90a7d2..ba89fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -140,7 +140,7 @@
         createDialogs();
 
         if (!mDialogs.isEmpty()) {
-            getComponent(CommandQueue.class).addCallbacks(this);
+            getComponent(CommandQueue.class).addCallback(this);
             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 416cc59..5c259d5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -342,13 +342,16 @@
                 }
             }
         }
+        boolean isCall = Notification.CATEGORY_CALL.equals(n.getNotification().category)
+                && n.isOngoing();
+        boolean isMusic = n.getNotification().hasMediaSession();
+        boolean isImportantOngoing = isMusic || isCall;
 
         Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
-        boolean isMessageType = Notification.MessagingStyle.class.equals(style)
-                || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
-                || hasRemoteInput;
-        return (isMessageType && autoBubbleMessages)
-                || (n.isOngoing() && autoBubbleOngoing)
+        boolean isMessageType = Notification.CATEGORY_MESSAGE.equals(n.getNotification().category);
+        boolean isMessageStyle = Notification.MessagingStyle.class.equals(style);
+        return (((isMessageType && hasRemoteInput) || isMessageStyle) && autoBubbleMessages)
+                || (isImportantOngoing && autoBubbleOngoing)
                 || autoBubbleAll;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
deleted file mode 100644
index 09c000b..0000000
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.car;
-
-import android.content.Context;
-import android.util.ArrayMap;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.car.CarFacetButtonController;
-import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-
-/**
- * Class factory to provide car specific SystemUI components.
- */
-public class CarSystemUIFactory extends SystemUIFactory {
-
-    public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
-            ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
-        return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
-    }
-
-    @Override
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
-            Context context) {
-        super.injectDependencies(providers, context);
-        providers.put(NotificationEntryManager.class,
-                () -> new CarNotificationEntryManager(context));
-        providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
-        providers.put(HvacController.class, () -> new HvacController(context));
-        providers.put(NotificationMediaManager.class,
-                () -> new CarNotificationMediaManager(context));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 2c61da3..1718cff 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -108,7 +108,7 @@
                 UserHandle.USER_ALL);
 
         updateConfiguration();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
     public static FalsingManager getInstance(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index bf8e04d..7e77843 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -401,7 +401,9 @@
                 }
                 mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
                         event.values);
-                updateListener();  // reregister, this sensor only fires once
+                if (!mRegistered) {
+                    updateListener();  // reregister, this sensor only fires once
+                }
             }));
         }
 
@@ -440,7 +442,9 @@
                 mRegistered = false;
                 mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
                         event.getValues());
-                updateListener();  // reregister, this sensor only fires once
+                if (!mRegistered) {
+                    updateListener();  // reregister, this sensor only fires once
+                }
             }));
         };
 
@@ -487,7 +491,6 @@
             }
             return sb.append(']').toString();
         }
-
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bad0148..afe9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -184,6 +184,7 @@
             if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
             mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
         }
+
         if (far && (paused || pausing)) {
             if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
             mMachine.requestState(DozeMachine.State.DOZE_AOD);
@@ -205,13 +206,13 @@
                     // In pocket, drop event.
                     return;
                 }
-                if (pausing || paused) {
+                if (mMachine.getState() == DozeMachine.State.DOZE) {
                     mMachine.requestState(DozeMachine.State.DOZE_AOD);
                 }
             }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
         } else {
             if (!pausing && !paused) {
-                mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+                mMachine.requestState(DozeMachine.State.DOZE);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index aa08562..e8ef454 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -45,7 +45,7 @@
                 .withCallback(this::onExtensionCallback)
                 .build();
         mPlugin = mExtension.get();
-        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
+        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
     }
 
     private void onExtensionCallback(GlobalActions newPlugin) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 0394998..dc11b4c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -55,12 +55,12 @@
         mContext = context;
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);
+        SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this);
     }
 
     @Override
     public void destroy() {
-        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
         if (mGlobalActions != null) {
             mGlobalActions.destroy();
             mGlobalActions = null;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3b9110d..9ccdf79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -488,6 +488,9 @@
                             // MVNO SIMs can become transiently NOT_READY when switching networks,
                             // so we should only lock when they are ABSENT.
                             onSimAbsentLocked();
+                            if (simWasLocked) {
+                                resetStateLocked();
+                            }
                         }
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 7792e17..37c8163 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -58,7 +58,7 @@
                 : com.android.systemui.pip.phone.PipManager.getInstance();
         mPipManager.initialize(mContext);
 
-        getComponent(CommandQueue.class).addCallbacks(this);
+        getComponent(CommandQueue.class).addCallback(this);
         putComponent(PipUI.class, this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 953eb70..2acbea4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -108,12 +108,12 @@
                 mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
             }
         }
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
     }
 
     @Override
     public void onDestroyView() {
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
+        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallback(this);
         super.onDestroyView();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index e2e943a..d7d3981 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -79,7 +79,7 @@
         mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
         mFooterIconId = R.drawable.ic_info_outline;
         mContext = context;
-        mMainHandler = new Handler(Looper.getMainLooper());
+        mMainHandler = new Handler(Looper.myLooper());
         mActivityStarter = Dependency.get(ActivityStarter.class);
         mSecurityController = Dependency.get(SecurityController.class);
         mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index d1c2df5..ca8e824 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -308,7 +308,7 @@
     protected static List<String> loadTileSpecs(Context context, String tileList) {
         final Resources res = context.getResources();
         final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
-        if (tileList == null) {
+        if (TextUtils.isEmpty(tileList)) {
             tileList = res.getString(R.string.quick_settings_tiles);
             if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 427f638..3cecff0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -232,6 +232,8 @@
         // Tint for the battery icons are handled in setupHost()
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_OFF);
+        // Don't need to worry about tuner settings for this icon
+        mBatteryRemainingIcon.setIgnoreTunerUpdates(true);
 
         mBatteryRemainingText = findViewById(R.id.batteryRemainingText);
         mBatteryRemainingText.setTextColor(fillColor);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0702d74..f13b565 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -37,7 +37,7 @@
 
     @Override
     public void start() {
-        getComponent(CommandQueue.class).addCallbacks(this);
+        getComponent(CommandQueue.class).addCallback(this);
         putComponent(Recents.class, this);
         mImpl = createRecentsImplementationFromConfig();
         mImpl.onStart(mContext, this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8b93995..95019ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -35,6 +35,8 @@
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.policy.CallbackController;
 
 import java.util.ArrayList;
 
@@ -45,7 +47,7 @@
  * coalescing these calls so they don't stack up.  For the calls
  * are coalesced, note that they are all idempotent.
  */
-public class CommandQueue extends IStatusBar.Stub {
+public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> {
     private static final int INDEX_MASK = 0xffff;
     private static final int MSG_SHIFT  = 16;
     private static final int MSG_MASK   = 0xffff << MSG_SHIFT;
@@ -183,12 +185,12 @@
                 && !ONLY_CORE_APPS;
     }
 
-    public void addCallbacks(Callbacks callbacks) {
+    public void addCallback(Callbacks callbacks) {
         mCallbacks.add(callbacks);
         callbacks.disable(mDisable1, mDisable2, false /* animate */);
     }
 
-    public void removeCallbacks(Callbacks callbacks) {
+    public void removeCallback(Callbacks callbacks) {
         mCallbacks.remove(callbacks);
     }
 
@@ -223,7 +225,9 @@
         }
     }
 
-    public void disable(int state1, int state2) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void disable(int displayId, int state1, int state2) {
         disable(state1, state2, true);
     }
 
@@ -266,8 +270,10 @@
         }
     }
 
-    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
         synchronized (mLock) {
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
@@ -282,7 +288,9 @@
         }
     }
 
-    public void topAppWindowChanged(boolean menuVisible) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void topAppWindowChanged(int displayId, boolean menuVisible) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
             mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
@@ -290,7 +298,9 @@
         }
     }
 
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -371,7 +381,9 @@
         }
     }
 
-    public void setWindowState(int window, int state) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void setWindowState(int displayId, int window, int state) {
         synchronized (mLock) {
             // don't coalesce these
             mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
@@ -385,7 +397,9 @@
         }
     }
 
-    public void appTransitionPending() {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void appTransitionPending(int displayId) {
         appTransitionPending(false /* forced */);
     }
 
@@ -395,13 +409,17 @@
         }
     }
 
-    public void appTransitionCancelled() {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void appTransitionCancelled(int displayId) {
         synchronized (mLock) {
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
         }
     }
 
-    public void appTransitionStarting(long startTime, long duration) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void appTransitionStarting(int displayId, long startTime, long duration) {
         appTransitionStarting(startTime, duration, false /* forced */);
     }
 
@@ -412,8 +430,9 @@
         }
     }
 
+    // TODO(b/117478341): Add multi-display support.
     @Override
-    public void appTransitionFinished() {
+    public void appTransitionFinished(int displayId) {
         synchronized (mLock) {
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 960d221..7d1b640 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -156,7 +156,7 @@
                 new IntentFilter(Intent.ACTION_TIME_TICK), null,
                 Dependency.get(Dependency.TIME_TICK_HANDLER));
 
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     /**
@@ -167,7 +167,7 @@
      */
     public void destroy() {
         mContext.unregisterReceiver(mTickReceiver);
-        Dependency.get(StatusBarStateController.class).removeListener(this);
+        Dependency.get(StatusBarStateController.class).removeCallback(this);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index b0724b1..bba4369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -165,7 +165,7 @@
         mCurrentUserId = ActivityManager.getCurrentUser();
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
         mLockPatternUtils = new LockPatternUtils(context);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 0e9f950..3334f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,9 +15,6 @@
  */
 package com.android.systemui.statusbar;
 
-import android.content.Intent;
-
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -39,12 +36,6 @@
     boolean isPresenterFullyCollapsed();
 
     /**
-     * Runs the given intent. The presenter may want to run some animations or close itself when
-     * this happens.
-     */
-    void startNotificationGutsIntent(Intent intent, int appUid, ExpandableNotificationRow row);
-
-    /**
      * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
      */
     void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
@@ -74,8 +65,7 @@
     int getMaxNotificationsWhileLocked(boolean recompute);
 
     /**
-     * True if the presenter
-     * @return
+     * True if the presenter is currently locked.
      */
     default boolean isPresenterLocked() { return false; }
 
@@ -88,22 +78,4 @@
      * @return true if the shade is collapsing.
      */
     boolean isCollapsing();
-
-    /**
-     * @return true if the shade is collapsing to show an activity over the lock screen
-     */
-    default public boolean isCollapsingToShowActivityOverLockscreen() {
-        return false;
-    }
-
-    /**
-     * Get the {@link ActivityLaunchAnimator} from the presenter so it can be queried by
-     * {@link com.android.systemui.statusbar.phone.StatusBar}
-     * @return the current animator
-     * @deprecated This is only here for now because StatusBar is still the ActivityLaunchAnimator
-     * callback but shouldn't be.
-     */
-    default public ActivityLaunchAnimator getActivityLaunchAnimator() {
-        return null;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 7be5461..ba69f3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -111,7 +111,6 @@
     private final KeyguardManager mKeyguardManager;
 
     protected RemoteInputController mRemoteInputController;
-    protected NotificationPresenter mPresenter;
     protected NotificationLifetimeExtender.NotificationSafeToRemoveCallback
             mNotificationLifetimeFinishedCallback;
     protected IStatusBarService mBarService;
@@ -141,7 +140,7 @@
                 ActivityManager.getService().resumeAppSwitches();
             } catch (RemoteException e) {
             }
-            return mCallback.handleRemoteViewClick(pendingIntent, () -> {
+            return mCallback.handleRemoteViewClick(view, pendingIntent, () -> {
                 Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                 options.second.setLaunchWindowingMode(
                         WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
@@ -320,10 +319,8 @@
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
     }
 
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            Callback callback,
-            RemoteInputController.Delegate delegate) {
-        mPresenter = presenter;
+    /** Initializes this component with the provided dependencies. */
+    public void setUpWithCallback(Callback callback, RemoteInputController.Delegate delegate) {
         mCallback = callback;
         mRemoteInputController = new RemoteInputController(delegate);
         mRemoteInputController.addCallback(new RemoteInputController.Callback() {
@@ -666,11 +663,13 @@
          * Performs any special handling for a remote view click. The default behaviour can be
          * called through the defaultHandler parameter.
          *
+         * @param view
          * @param pendingIntent
          * @param defaultHandler
          * @return  true iff the click was handled
          */
-        boolean handleRemoteViewClick(PendingIntent pendingIntent, ClickHandler defaultHandler);
+        boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+                ClickHandler defaultHandler);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index e7b4904..6cec36a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -121,13 +121,13 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Dependency.get(StatusBarStateController.class)
-                .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
+                .addCallback(mStateListener, StatusBarStateController.RANK_SHELF);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
@@ -225,7 +225,7 @@
             }
             viewState.hasItemsInStableShelf = lastViewState.inShelf;
             viewState.hidden = !mAmbientState.isShadeExpanded()
-                    || mAmbientState.isQsCustomizerShowing();
+                    || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
             viewState.maxShelfEnd = maxShelfEnd;
         } else {
             viewState.hidden = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index daa2fd4..dc3a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -129,7 +129,7 @@
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
         mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
         mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
-        Dependency.get(StatusBarStateController.class).addListener(mStatusBarStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index eaf52cb..3f84416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -26,8 +26,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.CallbackController;
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
@@ -36,7 +38,7 @@
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
-public class StatusBarStateController {
+public class StatusBarStateController implements CallbackController<StateListener> {
     private static final String TAG = "SbStateController";
 
     private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
@@ -228,7 +230,7 @@
         return mLastState == StatusBarState.SHADE_LOCKED;
     }
 
-    public void addListener(StateListener listener) {
+    public void addCallback(StateListener listener) {
         synchronized (mListeners) {
             addListenerInternalLocked(listener, Integer.MAX_VALUE);
         }
@@ -244,7 +246,7 @@
      * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
      * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
      */
-    public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+    public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
         synchronized (mListeners) {
             addListenerInternalLocked(listener, rank);
         }
@@ -264,7 +266,7 @@
         mListeners.sort(mComparator);
     }
 
-    public void removeListener(StateListener listener) {
+    public void removeCallback(StateListener listener) {
         synchronized (mListeners) {
             mListeners.removeIf((it) -> it.listener.equals(listener));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
deleted file mode 100644
index bd32856..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package com.android.systemui.statusbar.car;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-
-/**
- * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
- * category. It can also render a indicator impling that there are more options of apps to launch
- * using this component. This is done with a "More icon" currently an arrow as defined in the layout
- * file. The class is to serve as an example.
- * Usage example: A button that allows a user to select a music app and indicate that there are
- * other music apps installed.
- */
-public class CarFacetButton extends LinearLayout {
-    private static final String FACET_FILTER_DELIMITER = ";";
-    /**
-     * Extra information to be sent to a helper to make the decision of what app to launch when
-     * clicked.
-     */
-    private static final String EXTRA_FACET_CATEGORIES = "categories";
-    private static final String EXTRA_FACET_PACKAGES = "packages";
-    private static final String EXTRA_FACET_ID = "filter_id";
-    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
-
-    private Context mContext;
-    private AlphaOptimizedImageButton mIcon;
-    private AlphaOptimizedImageButton mMoreIcon;
-    private boolean mSelected = false;
-    private String[] mComponentNames;
-    /** App categories that are to be used with this widget */
-    private String[] mFacetCategories;
-    /** App packages that are allowed to be used with this widget */
-    private String[] mFacetPackages;
-    private int mIconResourceId;
-    /**
-     * If defined in the xml this will be the icon that's rendered when the button is marked as
-     * selected
-     */
-    private int mSelectedIconResourceId;
-    private boolean mUseMoreIcon = true;
-    private float mSelectedAlpha = 1f;
-    private float mUnselectedAlpha = 1f;
-
-
-    public CarFacetButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        View.inflate(context, R.layout.car_facet_button, this);
-
-        // extract custom attributes
-        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
-        setupIntents(typedArray);
-        setupIcons(typedArray);
-        CarFacetButtonController carFacetButtonController = Dependency.get(
-                CarFacetButtonController.class);
-        carFacetButtonController.addFacetButton(this);
-
-    }
-
-    /**
-     * Reads the custom attributes to setup click handlers for this component.
-     */
-    private void setupIntents(TypedArray typedArray) {
-        String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
-        String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
-        String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
-        String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
-        String componentNameString =
-                typedArray.getString(R.styleable.CarFacetButton_componentNames);
-        try {
-            final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
-            intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
-
-            if (packageString != null) {
-                mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
-                intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
-            }
-            if (categoryString != null) {
-                mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
-                intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
-            }
-            if (componentNameString != null) {
-                mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
-            }
-
-            setOnClickListener(v -> {
-                intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
-                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-            });
-
-            if (longPressIntentString != null) {
-                final Intent longPressIntent = Intent.parseUri(longPressIntentString,
-                        Intent.URI_INTENT_SCHEME);
-                setOnLongClickListener(v -> {
-                    mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT);
-                    return true;
-                });
-            }
-        } catch (Exception e) {
-            throw new RuntimeException("Failed to attach intent", e);
-        }
-    }
-
-
-    private void setupIcons(TypedArray styledAttributes) {
-        mSelectedAlpha = styledAttributes.getFloat(
-                R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
-        mUnselectedAlpha = styledAttributes.getFloat(
-                R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
-        mIcon = findViewById(R.id.car_nav_button_icon);
-        mIcon.setScaleType(ImageView.ScaleType.CENTER);
-        mIcon.setClickable(false);
-        mIcon.setAlpha(mUnselectedAlpha);
-        mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
-        mIcon.setImageResource(mIconResourceId);
-        mSelectedIconResourceId = styledAttributes.getResourceId(
-                R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
-
-        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
-        mMoreIcon.setClickable(false);
-        mMoreIcon.setAlpha(mSelectedAlpha);
-        mMoreIcon.setVisibility(GONE);
-        mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
-    }
-
-    /**
-     * @return The app categories the component represents
-     */
-    public String[] getCategories() {
-        if (mFacetCategories == null) {
-            return new String[0];
-        }
-        return mFacetCategories;
-    }
-
-    /**
-     * @return The valid packages that should be considered.
-     */
-    public String[] getFacetPackages() {
-        if (mFacetPackages == null) {
-            return new String[0];
-        }
-        return mFacetPackages;
-    }
-
-    public String[] getComponentName() {
-        if (mComponentNames == null) {
-            return new String[0];
-        }
-        return mComponentNames;
-    }
-
-    /**
-     * Updates the alpha of the icons to "selected" and shows the "More icon"
-     * @param selected true if the view must be selected, false otherwise
-     */
-    public void setSelected(boolean selected) {
-        super.setSelected(selected);
-        setSelected(selected, selected);
-    }
-
-    /**
-     * Updates the visual state to let the user know if it's been selected.
-     * @param selected true if should update the alpha of the icon to selected, false otherwise
-     * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
-     *                     is ignored if the attribute useMoreIcon is set to false
-     */
-    public void setSelected(boolean selected, boolean showMoreIcon) {
-        mSelected = selected;
-        mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
-        mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
-        if (mUseMoreIcon) {
-            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f506753..f899863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -299,6 +299,10 @@
             return top;
         }
 
+        public int getBottom() {
+            return bottom;
+        }
+
         public int getWidth() {
             return right - left;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
new file mode 100644
index 0000000..ba1b23b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
@@ -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.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.content.Intent;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Component responsible for handling actions on a notification which cause activites to start.
+ * (e.g. clicking on a notification, tapping on the settings icon in the notification guts)
+ */
+public interface NotificationActivityStarter {
+    /** Called when the user clicks on the surface of a notification. */
+    void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
+
+    /** Called when the user clicks on a button in the notification guts which fires an intent. */
+    void startNotificationGutsIntent(Intent intent, int appUid,
+            ExpandableNotificationRow row);
+
+    default boolean isCollapsingToShowActivityOverLockscreen() {
+        return false;
+    }
+}
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 70d144e..f4e7913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -143,6 +143,7 @@
     protected IStatusBarService mBarService;
     private NotificationPresenter mPresenter;
     private Callback mCallback;
+    private NotificationActivityStarter mNotificationActivityStarter;
     protected PowerManager mPowerManager;
     private NotificationListenerService.RankingMap mLatestRankingMap;
     protected HeadsUpManager mHeadsUpManager;
@@ -193,7 +194,7 @@
                 mBubbleController.collapseStack();
             }
 
-            mCallback.onNotificationClicked(sbn, row);
+            mNotificationActivityStarter.onNotificationClicked(sbn, row);
         }
 
         private boolean isMenuVisible(ExpandableNotificationRow row) {
@@ -346,6 +347,11 @@
         mOnAppOpsClickListener = mGutsManager::openGuts;
     }
 
+    public void setNotificationActivityStarter(
+            NotificationActivityStarter notificationActivityStarter) {
+        mNotificationActivityStarter = notificationActivityStarter;
+    }
+
     public NotificationData getNotificationData() {
         return mNotificationData;
     }
@@ -1247,15 +1253,6 @@
          */
         void onNotificationRemoved(String key, StatusBarNotification old);
 
-
-        /**
-         * Called when a notification is clicked.
-         *
-         * @param sbn notification that was clicked
-         * @param row row for that notification
-         */
-        void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row);
-
         /**
          * Called when a new notification and row is created.
          *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 1f48c15..09eb8a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -75,6 +75,6 @@
     /** Returns the value of the new interruption model setting. */
     public static boolean useNewInterruptionModel(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
-                NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) != 0;
+                NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) != 0;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 87313b8..9f02e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -149,7 +149,7 @@
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         // Not expected to be destroyed, don't need to unsubscribe
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     public void setUpWithContainer(NotificationListContainer listContainer) {
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 63fe04a..8214ea6 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
@@ -328,6 +328,7 @@
     private float mTranslationWhenRemoved;
     private boolean mWasChildInGroupWhenRemoved;
     private int mNotificationColorAmbient;
+    private NotificationInlineImageResolver mImageResolver;
 
     private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
             new SystemNotificationAsyncTask();
@@ -1621,6 +1622,8 @@
         mFalsingManager = FalsingManager.getInstance(context);
         mNotificationInflater = new NotificationInflater(this);
         mMenuRow = new NotificationMenuRow(mContext);
+        mImageResolver = new NotificationInlineImageResolver(context,
+                new NotificationInlineImageCache());
         initDimens();
     }
 
@@ -1657,6 +1660,10 @@
                 res.getBoolean(R.bool.config_showGroupNotificationBgWhenExpanded);
     }
 
+    NotificationInlineImageResolver getImageResolver() {
+        return mImageResolver;
+    }
+
     /**
      * Resets this view so it can be re-used for an updated notification.
      */
@@ -2008,7 +2015,8 @@
         float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
         int startClipTopAmount = params.getStartClipTopAmount();
         if (mNotificationParent != null) {
-            top -= mNotificationParent.getTranslationY();
+            float parentY = mNotificationParent.getTranslationY();
+            top -= parentY;
             mNotificationParent.setTranslationZ(translationZ);
             int parentStartClipTopAmount = params.getParentStartClipTopAmount();
             if (startClipTopAmount != 0) {
@@ -2018,8 +2026,12 @@
                 mNotificationParent.setClipTopAmount(clipTopAmount);
             }
             mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
-            mNotificationParent.setMinimumHeightForClipping(params.getHeight()
-                    + mNotificationParent.getActualHeight());
+            float clipBottom = Math.max(params.getBottom(),
+                    parentY + mNotificationParent.getActualHeight()
+                            - mNotificationParent.getClipBottomAmount());
+            float clipTop = Math.min(params.getTop(), parentY);
+            int minimumHeightForClipping = (int) (clipBottom - clipTop);
+            mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
         } else if (startClipTopAmount != 0) {
             int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
             setClipTopAmount(clipTopAmount);
@@ -2085,7 +2097,7 @@
 
     @Override
     protected boolean shouldClipToActualHeight() {
-        return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding;
+        return super.shouldClipToActualHeight() && !mExpandAnimationRunning;
     }
 
     @Override
@@ -3129,6 +3141,7 @@
         pw.print(", alpha: " + getAlpha());
         pw.print(", translation: " + getTranslation());
         pw.print(", removed: " + isRemoved());
+        pw.print(", expandAnimationRunning: " + mExpandAnimationRunning);
         NotificationContentView showingLayout = getShowingLayout();
         pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
         pw.println();
@@ -3142,6 +3155,11 @@
         pw.println();
         pw.println();
         if (mIsSummaryWithChildren) {
+            pw.print("  ChildrenContainer");
+            pw.print(" visibility: " + mChildrenContainer.getVisibility());
+            pw.print(", alpha: " + mChildrenContainer.getAlpha());
+            pw.print(", translationY: " + mChildrenContainer.getTranslationY());
+            pw.println();
             List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
             pw.println("  Children: " + notificationChildren.size());
             pw.println("  {");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 0efb130..6aadcb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -77,8 +77,6 @@
     protected boolean mShouldTranslateContents;
     private boolean mTopAmountRounded;
     private float mDistanceToTopRoundness = -1;
-    private float mExtraWidthForClipping;
-    private int mMinimumHeightForClipping = 0;
 
     private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
@@ -219,13 +217,15 @@
         return result;
     }
 
+    @Override
     public void setExtraWidthForClipping(float extraWidthForClipping) {
-        mExtraWidthForClipping = extraWidthForClipping;
+        super.setExtraWidthForClipping(extraWidthForClipping);
         invalidate();
     }
 
+    @Override
     public void setMinimumHeightForClipping(int minimumHeightForClipping) {
-        mMinimumHeightForClipping = minimumHeightForClipping;
+        super.setMinimumHeightForClipping(minimumHeightForClipping);
         invalidate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 1e8de07..20c4816 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -48,6 +48,8 @@
     private int mActualHeight;
     protected int mClipTopAmount;
     protected int mClipBottomAmount;
+    protected int mMinimumHeightForClipping = 0;
+    protected float mExtraWidthForClipping = 0;
     private boolean mDark;
     private ArrayList<View> mMatchParentViews = new ArrayList<View>();
     private static Rect mClipRect = new Rect();
@@ -398,14 +400,26 @@
     protected void updateClipping() {
         if (mClipToActualHeight && shouldClipToActualHeight()) {
             int top = getClipTopAmount();
-            mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding()
-                    - mClipBottomAmount, top));
+            int bottom = Math.max(Math.max(getActualHeight() + getExtraBottomPadding()
+                    - mClipBottomAmount, top), mMinimumHeightForClipping);
+            int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+            mClipRect.set(-halfExtraWidth, top, getWidth() + halfExtraWidth, bottom);
             setClipBounds(mClipRect);
         } else {
             setClipBounds(null);
         }
     }
 
+    public void setMinimumHeightForClipping(int minimumHeightForClipping) {
+        mMinimumHeightForClipping = minimumHeightForClipping;
+        updateClipping();
+    }
+
+    public void setExtraWidthForClipping(float extraWidthForClipping) {
+        mExtraWidthForClipping = extraWidthForClipping;
+        updateClipping();
+    }
+
     public float getHeaderVisibleAmount() {
         return 1.0f;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 3dc50ae..2e45527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -42,13 +41,13 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -79,13 +78,13 @@
             Dependency.get(StatusBarStateController.class);
     private final DeviceProvisionedController mDeviceProvisionedController =
             Dependency.get(DeviceProvisionedController.class);
-    private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
 
     // which notification is currently being longpress-examined by the user
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
     private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
     private NotificationPresenter mPresenter;
+    private NotificationActivityStarter mNotificationActivityStarter;
     private NotificationListContainer mListContainer;
     private CheckSaveListener mCheckSaveListener;
     private OnSettingsClickListener mOnSettingsClickListener;
@@ -94,8 +93,6 @@
 
     public NotificationGutsManager(Context context) {
         mContext = context;
-        Resources res = context.getResources();
-
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
@@ -109,6 +106,11 @@
         mOnSettingsClickListener = onSettingsClick;
     }
 
+    public void setNotificationActivityStarter(
+            NotificationActivityStarter notificationActivityStarter) {
+        mNotificationActivityStarter = notificationActivityStarter;
+    }
+
     public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
         setExposedGuts(entry.getGuts());
         bindGuts(entry.getRow());
@@ -127,7 +129,7 @@
         if (channel != null) {
             intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
         }
-        mPresenter.startNotificationGutsIntent(intent, appUid, row);
+        mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
     }
 
     protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
@@ -138,12 +140,12 @@
             } else {
                 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                 intent.setData(Uri.fromParts("package", pkg, null));
-                mPresenter.startNotificationGutsIntent(intent, uid, row);
+                mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
             }
         } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
             Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
-            mPresenter.startNotificationGutsIntent(intent, uid, row);
+            mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
         }
     }
 
@@ -262,7 +264,8 @@
                 (View v, Intent intent) -> {
                     mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
                     guts.resetFalsingCheck();
-                    mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
+                    mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
+                            row);
                 };
         boolean isForBlockingHelper = row.isBlockingHelperShowing();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 70860258..9908049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -33,6 +33,7 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.ImageMessageConsumer;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
@@ -114,7 +115,7 @@
     @InflationFlag
     private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
 
-    private static final InflationExecutor EXECUTOR = new InflationExecutor();
+    static final InflationExecutor EXECUTOR = new InflationExecutor();
 
     private final ExpandableNotificationRow mRow;
     private boolean mIsLowPriority;
@@ -244,6 +245,10 @@
         // Only inflate the ones that are set.
         reInflateFlags &= mInflationFlags;
         StatusBarNotification sbn = mRow.getEntry().notification;
+
+        // To check if the notification has inline image and preload inline image if necessary.
+        mRow.getImageResolver().preloadImages(sbn.getNotification());
+
         AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
                 mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
                 mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler);
@@ -520,8 +525,14 @@
             }
             return;
         }
-        RemoteViews.OnViewAppliedListener listener
-                = new RemoteViews.OnViewAppliedListener() {
+        RemoteViews.OnViewAppliedListener listener = new RemoteViews.OnViewAppliedListener() {
+
+            @Override
+            public void onViewInflated(View v) {
+                if (v instanceof ImageMessageConsumer) {
+                    ((ImageMessageConsumer) v).setImageResolver(row.getImageResolver());
+                }
+            }
 
             @Override
             public void onViewApplied(View v) {
@@ -697,7 +708,7 @@
                         && newView.getPackage() != null
                         && newView.getPackage().equals(oldView.getPackage())
                         && newView.getLayoutId() == oldView.getLayoutId()
-                        && !oldView.isReapplyDisallowed());
+                        && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
     }
 
     public void setInflationCallback(InflationCallback callback) {
@@ -851,6 +862,10 @@
             mRow.getEntry().onInflationTaskFinished();
             mRow.onNotificationUpdated();
             mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
+
+            // Notify the resolver that the inflation task has finished,
+            // try to purge unnecessary cached entries.
+            mRow.getImageResolver().purgeCache();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
new file mode 100644
index 0000000..8c8bad2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -0,0 +1,97 @@
+/*
+ * 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.systemui.statusbar.notification.row;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A cache for inline images of image messages.
+ */
+public class NotificationInlineImageCache implements NotificationInlineImageResolver.ImageCache {
+    private static final String TAG = NotificationInlineImageCache.class.getSimpleName();
+
+    private NotificationInlineImageResolver mResolver;
+    private final ConcurrentHashMap<Uri, PreloadImageTask> mCache;
+
+    public NotificationInlineImageCache() {
+        mCache = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public void setImageResolver(NotificationInlineImageResolver resolver) {
+        mResolver = resolver;
+    }
+
+    @Override
+    public boolean hasEntry(Uri uri) {
+        return mCache.containsKey(uri);
+    }
+
+    @Override
+    public void preload(Uri uri) {
+        PreloadImageTask newTask = new PreloadImageTask(mResolver);
+        newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri);
+        mCache.put(uri, newTask);
+    }
+
+    @Override
+    public Drawable get(Uri uri) {
+        Drawable result = null;
+        try {
+            result = mCache.get(uri).get();
+        } catch (InterruptedException | ExecutionException ex) {
+            Log.d(TAG, "get: Failed get image from " + uri);
+        }
+        return result;
+    }
+
+    @Override
+    public void purge() {
+        Set<Uri> wantedSet = mResolver.getWantedUriSet();
+        mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey()));
+    }
+
+    private static class PreloadImageTask extends AsyncTask<Uri, Void, Drawable> {
+        private final NotificationInlineImageResolver mResolver;
+
+        PreloadImageTask(NotificationInlineImageResolver resolver) {
+            mResolver = resolver;
+        }
+
+        @Override
+        protected Drawable doInBackground(Uri... uris) {
+            Drawable drawable = null;
+            Uri target = uris[0];
+
+            try {
+                drawable = mResolver.resolveImage(target);
+            } catch (IOException ex) {
+                Log.d(TAG, "PreloadImageTask: Resolve failed from " + target);
+            }
+
+            return drawable;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
new file mode 100644
index 0000000..588246f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -0,0 +1,197 @@
+/*
+ * 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.systemui.statusbar.notification.row;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.android.internal.widget.ImageResolver;
+import com.android.internal.widget.LocalImageResolver;
+import com.android.internal.widget.MessagingMessage;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Custom resolver with built-in image cache for image messages.
+ */
+public class NotificationInlineImageResolver implements ImageResolver {
+    private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
+
+    private final Context mContext;
+    private final ImageCache mImageCache;
+    private Set<Uri> mWantedUriSet;
+
+    /**
+     * Constructor.
+     * @param context    Context.
+     * @param imageCache The implementation of internal cache.
+     */
+    public NotificationInlineImageResolver(Context context, ImageCache imageCache) {
+        mContext = context.getApplicationContext();
+        mImageCache = imageCache;
+
+        if (mImageCache != null) {
+            mImageCache.setImageResolver(this);
+        }
+    }
+
+    /**
+     * Check if this resolver has its internal cache implementation.
+     * @return True if has its internal cache, false otherwise.
+     */
+    public boolean hasCache() {
+        return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
+    }
+
+    /**
+     * To resolve image from specified uri directly.
+     * @param uri Uri of the image.
+     * @return Drawable of the image.
+     * @throws IOException Throws if failed at resolving the image.
+     */
+    Drawable resolveImage(Uri uri) throws IOException {
+        return LocalImageResolver.resolveImage(uri, mContext);
+    }
+
+    @Override
+    public Drawable loadImage(Uri uri) {
+        Drawable result = null;
+        try {
+            result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
+        } catch (IOException ex) {
+            Log.d(TAG, "loadImage: Can't load image from " + uri);
+        }
+        return result;
+    }
+
+    /**
+     * Resolve the message list from specified notification and
+     * refresh internal cache according to the result.
+     * @param notification The Notification to be resolved.
+     */
+    public void preloadImages(Notification notification) {
+        if (!hasCache()) {
+            return;
+        }
+
+        retrieveWantedUriSet(notification);
+        Set<Uri> wantedSet = getWantedUriSet();
+        wantedSet.forEach(uri -> {
+            if (!mImageCache.hasEntry(uri)) {
+                // The uri is not in the cache, we need trigger a loading task for it.
+                mImageCache.preload(uri);
+            }
+        });
+    }
+
+    /**
+     * Try to purge unnecessary cache entries.
+     */
+    public void purgeCache() {
+        if (!hasCache()) {
+            return;
+        }
+        mImageCache.purge();
+    }
+
+    private void retrieveWantedUriSet(Notification notification) {
+        Parcelable[] messages;
+        Parcelable[] historicMessages;
+        List<Notification.MessagingStyle.Message> messageList;
+        List<Notification.MessagingStyle.Message> historicList;
+        Set<Uri> result = new HashSet<>();
+
+        Bundle extras = notification.extras;
+        if (extras == null) {
+            return;
+        }
+
+        messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+        messageList = messages == null ? null :
+                Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+        if (messageList != null) {
+            for (Notification.MessagingStyle.Message message : messageList) {
+                if (MessagingMessage.hasImage(message)) {
+                    result.add(message.getDataUri());
+                }
+            }
+        }
+
+        historicMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
+        historicList = historicMessages == null ? null :
+                Notification.MessagingStyle.Message.getMessagesFromBundleArray(historicMessages);
+        if (historicList != null) {
+            for (Notification.MessagingStyle.Message historic : historicList) {
+                if (MessagingMessage.hasImage(historic)) {
+                    result.add(historic.getDataUri());
+                }
+            }
+        }
+
+        mWantedUriSet = result;
+    }
+
+    Set<Uri> getWantedUriSet() {
+        return mWantedUriSet;
+    }
+
+    /**
+     * A interface for internal cache implementation of this resolver.
+     */
+    interface ImageCache {
+        /**
+         * Load the image from cache first then resolve from uri if missed the cache.
+         * @param uri The uri of the image.
+         * @return Drawable of the image.
+         */
+        Drawable get(Uri uri);
+
+        /**
+         * Set the image resolver that actually resolves image from specified uri.
+         * @param resolver The resolver implementation that resolves image from specified uri.
+         */
+        void setImageResolver(NotificationInlineImageResolver resolver);
+
+        /**
+         * Check if the uri is in the cache no matter it is loading or loaded.
+         * @param uri The uri to check.
+         * @return True if it is already in the cache; false otherwise.
+         */
+        boolean hasEntry(Uri uri);
+
+        /**
+         * Start a new loading task for the target uri.
+         * @param uri The target to load.
+         */
+        void preload(Uri uri);
+
+        /**
+         * Purge unnecessary entries in the cache.
+         */
+        void purge();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 948d2a5..50564e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -267,7 +267,9 @@
         } else {
             mRightMenuItems.add(mInfoItem);
             mRightMenuItems.add(mAppOpsItem);
-            mRightMenuItems.add(mSnoozeItem);
+            if (!isForeground) {
+                mRightMenuItems.add(mSnoozeItem);
+            }
         }
 
         populateMenuViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 5d640e0..eca1a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -618,7 +618,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Dependency.get(StatusBarStateController.class)
-                .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
+                .addCallback(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
@@ -626,7 +626,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 3b13fe9..24570ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -120,15 +120,15 @@
     @Override
     public void onResume() {
         super.onResume();
-        mCommandQueue.addCallbacks(this);
-        mStatusBarStateController.addListener(this);
+        mCommandQueue.addCallback(this);
+        mStatusBarStateController.addCallback(this);
     }
 
     @Override
     public void onPause() {
         super.onPause();
-        mCommandQueue.removeCallbacks(this);
-        mStatusBarStateController.removeListener(this);
+        mCommandQueue.removeCallback(this);
+        mStatusBarStateController.removeCallback(this);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 94b2cde..cfa751c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -88,7 +88,7 @@
     public DozeScrimController(DozeParameters dozeParameters) {
         mDozeParameters = dozeParameters;
         //Never expected to be destroyed
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 9faada0..aa0b7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -129,7 +129,7 @@
                 updateTouchableRegionListener();
             }
         });
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
         mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
             if (!hasBubbles) {
                 mBubbleGoingAway = true;
@@ -143,7 +143,7 @@
     }
 
     public void destroy() {
-        Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
     private void initResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b29889d..57cc7d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -73,15 +73,15 @@
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
-                .addCallbacks(this);
-        mStatusBarStateController.addListener(this);
+                .addCallback(this);
+        mStatusBarStateController.addCallback(this);
         mDozeAmount = mStatusBarStateController.getDozeAmount();
     }
 
     public void destroy(Context context) {
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
-                .removeCallbacks(this);
-        mStatusBarStateController.removeListener(this);
+                .removeCallback(this);
+        mStatusBarStateController.removeCallback(this);
     }
 
     public void saveState(Bundle outState) {
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 8657003..ae0a145 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -32,7 +32,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.Fragment;
 import android.app.IActivityTaskManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
@@ -90,6 +89,7 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyButtonView;
+import com.android.systemui.util.LifecycleFragment;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -101,7 +101,7 @@
  * Fragment containing the NavigationBarFragment. Contains logic for what happens
  * on clicks and view states of the nav bar.
  */
-public class NavigationBarFragment extends Fragment implements Callbacks {
+public class NavigationBarFragment extends LifecycleFragment implements Callbacks {
 
     public static final String TAG = "NavigationBar";
     private static final boolean DEBUG = false;
@@ -199,7 +199,7 @@
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
-        mCommandQueue.addCallbacks(this);
+        mCommandQueue.observe(getLifecycle(), this);
         mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
         mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
         mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
@@ -225,7 +225,6 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mCommandQueue.removeCallbacks(this);
         Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
                 mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mMagnificationObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 12a0cc8..3984405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -24,7 +24,6 @@
 import android.view.Display;
 import android.view.IWallpaperVisibilityListener;
 import android.view.IWindowManager;
-import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -165,23 +164,4 @@
         }
         mView.onDarkIntensityChange(darkIntensity);
     }
-
-    private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View v, MotionEvent ev) {
-            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                // even though setting the systemUI visibility below will turn these views
-                // on, we need them to come up faster so that they can catch this motion
-                // event
-                applyLightsOut(false, false, false);
-
-                try {
-                    mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
-                            "LightsOutListener");
-                } catch (android.os.RemoteException ex) {
-                }
-            }
-            return false;
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3e31fa0..2a68fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -82,7 +82,7 @@
     private boolean mIsDozing;
 
     public NotificationGroupAlertTransferHelper() {
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 448b5c3..8f4369a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -55,7 +55,7 @@
     private boolean mIsUpdatingUnchangedGroup;
 
     public NotificationGroupManager() {
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 33d176a..a2a11bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -366,7 +366,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
         Dependency.get(ZenModeController.class).addCallback(this);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
@@ -375,7 +375,7 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
-        Dependency.get(StatusBarStateController.class).removeListener(this);
+        Dependency.get(StatusBarStateController.class).removeCallback(this);
         Dependency.get(ZenModeController.class).removeCallback(this);
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index c84f3db..ee1eb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -268,7 +268,7 @@
         mLocationController.addCallback(this);
         mPrivacyItemController.setListening(true);
 
-        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
+        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
 
         // Clear out all old notifications on startup (only present in the case where sysui dies)
@@ -296,7 +296,7 @@
         mKeyguardMonitor.removeCallback(this);
         mLocationController.removeCallback(this);
         mPrivacyItemController.setListening(false);
-        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
         mContext.unregisterReceiver(mIntentReceiver);
 
         NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 2cbf27c..4983618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -185,6 +185,7 @@
 
         // Requires proxy and an active gesture or able to perform any gesture to continue
         if (mOverviewEventSender.getProxy() == null
+                || !mOverviewEventSender.shouldShowSwipeUpUI()
                 || (mCurrentAction == null && !canPerformAnyAction())) {
             return deadZoneConsumed;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5723948..bf53b77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -161,7 +161,6 @@
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -188,6 +187,7 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -579,7 +579,10 @@
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private boolean mVibrateOnOpening;
     private VibratorHelper mVibratorHelper;
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
     protected NotificationPresenter mPresenter;
+    private NotificationActivityStarter mNotificationActivityStarter;
+    private boolean mPulsing;
 
     @Override
     public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -628,7 +631,7 @@
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
         mColorExtractor.addOnColorsChangedListener(this);
-        mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
+        mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -664,7 +667,7 @@
 
         // Connect in to the status bar manager service
         mCommandQueue = getComponent(CommandQueue.class);
-        mCommandQueue.addCallbacks(this);
+        mCommandQueue.addCallback(this);
 
         int[] switches = new int[9];
         ArrayList<IBinder> binders = new ArrayList<>();
@@ -920,8 +923,7 @@
                     Dependency.get(ExtensionController.class)
                             .newExtension(QS.class)
                             .withPlugin(QS.class)
-                            .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
-                            .withDefault(QSFragment::new)
+                            .withDefault(this::createDefaultQSFragment)
                             .build());
             final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
                     mIconController);
@@ -1009,15 +1011,29 @@
         ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
     }
 
+    protected QS createDefaultQSFragment() {
+        return new QSFragment();
+    }
+
     protected void setUpPresenter() {
         // Set up the initial notification state.
+        mActivityLaunchAnimator = new ActivityLaunchAnimator(
+                mStatusBarWindow, this, mNotificationPanel,
+                (NotificationListContainer) mStackScroller);
+
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
-                mScrimController, this);
+                mScrimController, mActivityLaunchAnimator);
+
         mAppOpsController.addCallback(APP_OPS, this);
         mNotificationListener.setUpWithPresenter(mPresenter);
         mNotificationShelf.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
+
+        mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
+                mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
+        mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+        mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter);
     }
 
     /**
@@ -1542,7 +1558,7 @@
     }
 
     public boolean isPulsing() {
-        return mAmbientPulseManager.hasNotifications();
+        return mPulsing;
     }
 
     public boolean isLaunchTransitionFadingAway() {
@@ -1636,9 +1652,9 @@
 
     @Override
     public void onExpandAnimationTimedOut() {
-        ActivityLaunchAnimator animator = mPresenter.getActivityLaunchAnimator();
         if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
-                && animator != null && !animator.isLaunchForActivity()) {
+                && mActivityLaunchAnimator != null
+                && !mActivityLaunchAnimator.isLaunchForActivity()) {
             onClosingFinished();
         } else {
             collapsePanel(true /* animate */);
@@ -1937,7 +1953,7 @@
 
         runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-        if (!mPresenter.isCollapsingToShowActivityOverLockscreen()) {
+        if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
             showBouncerIfKeyguard();
         } else if (DEBUG) {
             Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
@@ -2524,7 +2540,7 @@
                 }
             }
             if (dismissShade) {
-                if (mExpandedVisible) {
+                if (mExpandedVisible && !mBouncerShowing) {
                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
                             true /* delayed*/);
                 } else {
@@ -2887,7 +2903,7 @@
         mContext.unregisterReceiver(mDemoReceiver);
         mAssistManager.destroy();
         mHeadsUpManager.destroy();
-        mStatusBarStateController.removeListener(this);
+        mStatusBarStateController.removeCallback(this);
 
         if (mQSPanel != null && mQSPanel.getHost() != null) {
             mQSPanel.getHost().destroy();
@@ -3205,6 +3221,7 @@
         mNotificationPanel.onAffordanceLaunchEnded();
         mNotificationPanel.animate().cancel();
         mNotificationPanel.setAlpha(1f);
+        updateScrimController();
         Trace.endSection();
         return staying;
     }
@@ -3933,6 +3950,10 @@
                 return;
             }
 
+            // Set the state to pulsing, so ScrimController will know what to do once we ask it to
+            // execute the transition. The pulse callback will then be invoked when the scrims
+            // are black, indicating that StatusBar is ready to present the rest of the UI.
+            mPulsing = true;
             mDozeScrimController.pulse(new PulseCallback() {
                 @Override
                 public void onPulseStarted() {
@@ -3946,6 +3967,7 @@
 
                 @Override
                 public void onPulseFinished() {
+                    mPulsing = false;
                     callback.onPulseFinished();
                     setPulsing(false);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 7c17c01..4f25349 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -76,7 +76,7 @@
         loadDimens();
 
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
-                .addCallbacks(this);
+                .addCallback(this);
         Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 484fe11..0f8970f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -151,7 +151,7 @@
         mLockPatternUtils = lockPatternUtils;
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     public void registerStatusBar(StatusBar statusBar,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
new file mode 100644
index 0000000..c93d151
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -0,0 +1,368 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.TaskStackBuilder;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.PreviewInflater;
+
+/**
+ * Status bar implementation of {@link NotificationActivityStarter}.
+ */
+public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
+
+    private static final String TAG = "NotificationClickHandler";
+
+    private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+    private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
+            (StatusBarRemoteInputCallback) Dependency.get(
+                    NotificationRemoteInputManager.Callback.class);
+    private final NotificationRemoteInputManager mRemoteInputManager =
+            Dependency.get(NotificationRemoteInputManager.class);
+    private final NotificationLockscreenUserManager mLockscreenUserManager =
+            Dependency.get(NotificationLockscreenUserManager.class);
+    private final ShadeController mShadeController = Dependency.get(ShadeController.class);
+    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+    private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
+    private final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
+
+    private final Context mContext;
+    private final NotificationPanelView mNotificationPanel;
+    private final NotificationPresenter mPresenter;
+    private final LockPatternUtils mLockPatternUtils;
+    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final KeyguardManager mKeyguardManager;
+    private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final IStatusBarService mBarService;
+    private final CommandQueue mCommandQueue;
+
+    private boolean mIsCollapsingToShowActivityOverLockscreen;
+
+    public StatusBarNotificationActivityStarter(Context context,
+            NotificationPanelView panel,
+            NotificationPresenter presenter,
+            HeadsUpManagerPhone headsUpManager,
+            ActivityLaunchAnimator activityLaunchAnimator) {
+        mContext = context;
+        mNotificationPanel = panel;
+        mPresenter = presenter;
+        mLockPatternUtils = new LockPatternUtils(context);
+        mHeadsUpManager = headsUpManager;
+        mKeyguardManager = context.getSystemService(KeyguardManager.class);
+        mActivityLaunchAnimator = activityLaunchAnimator;
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mCommandQueue = getComponent(context, CommandQueue.class);
+    }
+
+    /**
+     * Called when a notification is clicked.
+     *
+     * @param sbn notification that was clicked
+     * @param row row for that notification
+     */
+    @Override
+    public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
+        RemoteInputController controller = mRemoteInputManager.getController();
+        if (controller.isRemoteInputActive(row.getEntry())
+                && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
+            // We have an active remote input typed and the user clicked on the notification.
+            // this was probably unintentional, so we're closing the edit text instead.
+            controller.closeRemoteInputs();
+            return;
+        }
+        Notification notification = sbn.getNotification();
+        final PendingIntent intent = notification.contentIntent != null
+                ? notification.contentIntent
+                : notification.fullScreenIntent;
+        final String notificationKey = sbn.getKey();
+
+        boolean isActivityIntent = intent.isActivity();
+        final boolean afterKeyguardGone = isActivityIntent
+                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+                mLockscreenUserManager.getCurrentUserId());
+        final boolean wasOccluded = mShadeController.isOccluded();
+        boolean showOverLockscreen = mKeyguardMonitor.isShowing()
+                && PreviewInflater.wouldShowOverLockscreen(mContext,
+                intent.getIntent(),
+                mLockscreenUserManager.getCurrentUserId());
+        ActivityStarter.OnDismissAction postKeyguardAction =
+                () -> handleNotificationClickAfterKeyguardDismissed(
+                        sbn, row, controller, intent, notificationKey,
+                        isActivityIntent, wasOccluded, showOverLockscreen);
+        if (showOverLockscreen) {
+            mIsCollapsingToShowActivityOverLockscreen = true;
+            postKeyguardAction.onDismiss();
+        } else {
+            mActivityStarter.dismissKeyguardThenExecute(
+                    postKeyguardAction, null /* cancel */, afterKeyguardGone);
+        }
+    }
+
+    private boolean handleNotificationClickAfterKeyguardDismissed(
+            StatusBarNotification sbn,
+            ExpandableNotificationRow row,
+            RemoteInputController controller,
+            PendingIntent intent,
+            String notificationKey,
+            boolean isActivityIntent,
+            boolean wasOccluded,
+            boolean showOverLockscreen) {
+        // TODO: Some of this code may be able to move to NotificationEntryManager.
+        if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
+            // Release the HUN notification to the shade.
+
+            if (mPresenter.isPresenterFullyCollapsed()) {
+                HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+            }
+            //
+            // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+            // become canceled shortly by NoMan, but we can't assume that.
+            mHeadsUpManager.removeNotification(sbn.getKey(),
+                    true /* releaseImmediately */);
+        }
+        StatusBarNotification parentToCancel = null;
+        if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+            StatusBarNotification summarySbn =
+                    mGroupManager.getLogicalGroupSummary(sbn).notification;
+            if (shouldAutoCancel(summarySbn)) {
+                parentToCancel = summarySbn;
+            }
+        }
+        final StatusBarNotification parentToCancelFinal = parentToCancel;
+        final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
+                sbn, row, controller, intent, notificationKey,
+                isActivityIntent, wasOccluded, parentToCancelFinal);
+
+        if (showOverLockscreen) {
+            mShadeController.addPostCollapseAction(runnable);
+            mShadeController.collapsePanel(true /* animate */);
+        } else if (mKeyguardMonitor.isShowing()
+                && mShadeController.isOccluded()) {
+            mShadeController.addAfterKeyguardGoneRunnable(runnable);
+            mShadeController.collapsePanel();
+        } else {
+            new Thread(runnable).start();
+        }
+
+        return !mNotificationPanel.isFullyCollapsed();
+    }
+
+    private void handleNotificationClickAfterPanelCollapsed(
+            StatusBarNotification sbn,
+            ExpandableNotificationRow row,
+            RemoteInputController controller,
+            PendingIntent intent,
+            String notificationKey,
+            boolean isActivityIntent,
+            boolean wasOccluded,
+            StatusBarNotification parentToCancelFinal) {
+        try {
+            // The intent we are sending is for the application, which
+            // won't have permission to immediately start an activity after
+            // the user switches to home.  We know it is safe to do at this
+            // point, so make sure new activity switches are now allowed.
+            ActivityManager.getService().resumeAppSwitches();
+        } catch (RemoteException e) {
+        }
+        int launchResult;
+        // If we are launching a work activity and require to launch
+        // separate work challenge, we defer the activity action and cancel
+        // notification until work challenge is unlocked.
+        if (isActivityIntent) {
+            final int userId = intent.getCreatorUserHandle().getIdentifier();
+            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+                    && mKeyguardManager.isDeviceLocked(userId)) {
+                // TODO(b/28935539): should allow certain activities to
+                // bypass work challenge
+                if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
+                        intent.getIntentSender(), notificationKey)) {
+                    // Show work challenge, do not run PendingIntent and
+                    // remove notification
+                    collapseOnMainThread();
+                    return;
+                }
+            }
+        }
+        Intent fillInIntent = null;
+        NotificationData.Entry entry = row.getEntry();
+        CharSequence remoteInputText = null;
+        if (!TextUtils.isEmpty(entry.remoteInputText)) {
+            remoteInputText = entry.remoteInputText;
+        }
+        if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.key)) {
+            fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
+                    remoteInputText.toString());
+        }
+        RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
+                row, wasOccluded);
+        try {
+            if (adapter != null) {
+                ActivityTaskManager.getService()
+                        .registerRemoteAnimationForNextActivityStart(
+                                intent.getCreatorPackage(), adapter);
+            }
+            launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
+                    null, null, getActivityOptions(adapter));
+            mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
+        } catch (RemoteException | PendingIntent.CanceledException e) {
+            // the stack trace isn't very helpful here.
+            // Just log the exception message.
+            Log.w(TAG, "Sending contentIntent failed: " + e);
+
+            // TODO: Dismiss Keyguard.
+        }
+        if (isActivityIntent) {
+            mAssistManager.hideAssist();
+        }
+        if (shouldCollapse()) {
+            collapseOnMainThread();
+        }
+
+        final int count =
+                mEntryManager.getNotificationData().getActiveNotifications().size();
+        final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+        final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
+                rank, count, true);
+        try {
+            mBarService.onNotificationClick(notificationKey, nv);
+        } catch (RemoteException ex) {
+            // system process is dead if we're here.
+        }
+        if (parentToCancelFinal != null) {
+            removeNotification(parentToCancelFinal);
+        }
+        if (shouldAutoCancel(sbn)
+                || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+                notificationKey)) {
+            // Automatically remove all notifications that we may have kept around longer
+            removeNotification(sbn);
+        }
+        mIsCollapsingToShowActivityOverLockscreen = false;
+    }
+
+    @Override
+    public void startNotificationGutsIntent(final Intent intent, final int appUid,
+            ExpandableNotificationRow row) {
+        mActivityStarter.dismissKeyguardThenExecute(() -> {
+            AsyncTask.execute(() -> {
+                int launchResult = TaskStackBuilder.create(mContext)
+                        .addNextIntentWithParentStack(intent)
+                        .startActivities(getActivityOptions(
+                                mActivityLaunchAnimator.getLaunchAnimation(
+                                        row, mShadeController.isOccluded())),
+                                new UserHandle(UserHandle.getUserId(appUid)));
+                mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
+                if (shouldCollapse()) {
+                    // Putting it back on the main thread, since we're touching views
+                    Dependency.get(MAIN_HANDLER).post(() -> mCommandQueue.animateCollapsePanels(
+                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+                }
+            });
+            return true;
+        }, null, false /* afterKeyguardGone */);
+    }
+
+    @Override
+    public boolean isCollapsingToShowActivityOverLockscreen() {
+        return mIsCollapsingToShowActivityOverLockscreen;
+    }
+
+    private static boolean shouldAutoCancel(StatusBarNotification sbn) {
+        int flags = sbn.getNotification().flags;
+        if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
+            return false;
+        }
+        if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return false;
+        }
+        return true;
+    }
+
+    private void collapseOnMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            mShadeController.collapsePanel();
+        } else {
+            Dependency.get(MAIN_HANDLER).post(mShadeController::collapsePanel);
+        }
+    }
+
+    private boolean shouldCollapse() {
+        return mStatusBarStateController.getState() != StatusBarState.SHADE
+                || !mActivityLaunchAnimator.isAnimationPending();
+    }
+
+    private void removeNotification(StatusBarNotification notification) {
+        // We have to post it to the UI thread for synchronization
+        Dependency.get(MAIN_HANDLER).post(() -> {
+            Runnable removeRunnable =
+                    () -> mEntryManager.performRemoveNotification(notification);
+            if (mPresenter.isCollapsing()) {
+                // To avoid lags we're only performing the remove
+                // after the shade was collapsed
+                mShadeController.addPostCollapseAction(removeRunnable);
+            } else {
+                removeRunnable.run();
+            }
+        });
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 588c3a8..3550bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -14,35 +14,22 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.Dependency.MAIN_HANDLER;
 import static com.android.systemui.SysUiServiceProvider.getComponent;
 import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
 import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
 import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
-import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
 
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.TaskStackBuilder;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
-import android.view.RemoteAnimationAdapter;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
@@ -50,13 +37,10 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.AmbientPulseManager;
@@ -65,9 +49,7 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
@@ -80,9 +62,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.PreviewInflater;
 
 public class StatusBarNotificationPresenter implements NotificationPresenter {
 
@@ -93,7 +73,6 @@
 
     private final ShadeController mShadeController = Dependency.get(ShadeController.class);
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
-    private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
     private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
     private final NotificationViewHierarchyManager mViewHierarchyManager =
             Dependency.get(NotificationViewHierarchyManager.class);
@@ -105,12 +84,6 @@
             Dependency.get(NotificationEntryManager.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
-    private final NotificationRemoteInputManager mRemoteInputManager =
-            Dependency.get(NotificationRemoteInputManager.class);
-    private final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
-    private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
-            (StatusBarRemoteInputCallback) Dependency.get(Callback.class);
     protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
 
     private final NotificationPanelView mNotificationPanel;
@@ -122,7 +95,6 @@
     private final CommandQueue mCommandQueue;
 
     private final AccessibilityManager mAccessibilityManager;
-    private final LockPatternUtils mLockPatternUtils;
     private final KeyguardManager mKeyguardManager;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
     private final int mMaxAllowedKeyguardNotifications;
@@ -133,34 +105,32 @@
 
     protected boolean mVrMode;
     private int mMaxKeyguardNotifications;
-    private boolean mIsCollapsingToShowActivityOverLockscreen;
 
-    public StatusBarNotificationPresenter(Context context, NotificationPanelView panel,
-            HeadsUpManagerPhone headsUp, StatusBarWindowView statusBarWindow,
-            ViewGroup stackScroller, DozeScrimController dozeScrimController,
+    public StatusBarNotificationPresenter(Context context,
+            NotificationPanelView panel,
+            HeadsUpManagerPhone headsUp,
+            StatusBarWindowView statusBarWindow,
+            ViewGroup stackScroller,
+            DozeScrimController dozeScrimController,
             ScrimController scrimController,
-            ActivityLaunchAnimator.Callback launchAnimatorCallback) {
+            ActivityLaunchAnimator activityLaunchAnimator) {
         mContext = context;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
         mCommandQueue = getComponent(context, CommandQueue.class);
         mAboveShelfObserver = new AboveShelfObserver(stackScroller);
+        mActivityLaunchAnimator = activityLaunchAnimator;
         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
                 R.id.notification_container_parent));
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mDozeScrimController = dozeScrimController;
         mScrimController = scrimController;
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
-        mLockPatternUtils = new LockPatternUtils(context);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
         mMaxAllowedKeyguardNotifications = context.getResources().getInteger(
                 R.integer.keyguard_max_notification_count);
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mActivityLaunchAnimator = new ActivityLaunchAnimator(statusBarWindow,
-                launchAnimatorCallback,
-                mNotificationPanel,
-                (NotificationListContainer) stackScroller);
 
         if (MULTIUSER_DEBUG) {
             mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
@@ -176,10 +146,12 @@
                 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
             }
         }
-        mRemoteInputManager.setUpWithPresenter(this,
+        NotificationRemoteInputManager remoteInputManager =
+                Dependency.get(NotificationRemoteInputManager.class);
+        remoteInputManager.setUpWithCallback(
                 Dependency.get(NotificationRemoteInputManager.Callback.class),
                 mNotificationPanel.createRemoteInputDelegate());
-        mRemoteInputManager.getController().addCallback(
+        remoteInputManager.getController().addCallback(
                 Dependency.get(StatusBarWindowController.class));
 
         NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
@@ -204,11 +176,6 @@
     }
 
     @Override
-    public ActivityLaunchAnimator getActivityLaunchAnimator() {
-        return mActivityLaunchAnimator;
-    }
-
-    @Override
     public boolean isCollapsing() {
         return mNotificationPanel.isCollapsing()
                 || mActivityLaunchAnimator.isAnimationPending()
@@ -216,11 +183,6 @@
     }
 
     @Override
-    public boolean isCollapsingToShowActivityOverLockscreen() {
-        return mIsCollapsingToShowActivityOverLockscreen;
-    }
-
-    @Override
     public void onPerformRemoveNotification(StatusBarNotification n) {
         if (mNotificationPanel.hasPulsingNotifications() &&
                     !mAmbientPulseManager.hasNotifications()) {
@@ -377,201 +339,6 @@
     }
 
     @Override
-    public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
-        RemoteInputController controller = mRemoteInputManager.getController();
-        if (controller.isRemoteInputActive(row.getEntry())
-                && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
-            // We have an active remote input typed and the user clicked on the notification.
-            // this was probably unintentional, so we're closing the edit text instead.
-            controller.closeRemoteInputs();
-            return;
-        }
-        Notification notification = sbn.getNotification();
-        final PendingIntent intent = notification.contentIntent != null
-                ? notification.contentIntent
-                : notification.fullScreenIntent;
-        final String notificationKey = sbn.getKey();
-
-        boolean isActivityIntent = intent.isActivity();
-        final boolean afterKeyguardGone = isActivityIntent
-                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
-                mLockscreenUserManager.getCurrentUserId());
-        final boolean wasOccluded = mShadeController.isOccluded();
-        boolean showOverLockscreen = mKeyguardMonitor.isShowing()
-                && PreviewInflater.wouldShowOverLockscreen(mContext,
-                intent.getIntent(),
-                mLockscreenUserManager.getCurrentUserId());
-        OnDismissAction postKeyguardAction = () -> {
-            // TODO: Some of this code may be able to move to NotificationEntryManager.
-            if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
-                // Release the HUN notification to the shade.
-
-                if (isPresenterFullyCollapsed()) {
-                    HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
-                }
-                //
-                // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
-                // become canceled shortly by NoMan, but we can't assume that.
-                mHeadsUpManager.removeNotification(sbn.getKey(),
-                        true /* releaseImmediately */);
-            }
-            StatusBarNotification parentToCancel = null;
-            if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
-                StatusBarNotification summarySbn =
-                        mGroupManager.getLogicalGroupSummary(sbn).notification;
-                if (shouldAutoCancel(summarySbn)) {
-                    parentToCancel = summarySbn;
-                }
-            }
-            final StatusBarNotification parentToCancelFinal = parentToCancel;
-            final Runnable runnable = () -> {
-                try {
-                    // The intent we are sending is for the application, which
-                    // won't have permission to immediately start an activity after
-                    // the user switches to home.  We know it is safe to do at this
-                    // point, so make sure new activity switches are now allowed.
-                    ActivityManager.getService().resumeAppSwitches();
-                } catch (RemoteException e) {
-                }
-                int launchResult = ActivityManager.START_CANCELED;
-                if (intent != null) {
-                    // If we are launching a work activity and require to launch
-                    // separate work challenge, we defer the activity action and cancel
-                    // notification until work challenge is unlocked.
-                    if (isActivityIntent) {
-                        final int userId = intent.getCreatorUserHandle().getIdentifier();
-                        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
-                                && mKeyguardManager.isDeviceLocked(userId)) {
-                            // TODO(b/28935539): should allow certain activities to
-                            // bypass work challenge
-                            if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
-                                    intent.getIntentSender(), notificationKey)) {
-                                // Show work challenge, do not run PendingIntent and
-                                // remove notification
-                                collapseOnMainThread();
-                                return;
-                            }
-                        }
-                    }
-                    Intent fillInIntent = null;
-                    Entry entry = row.getEntry();
-                    CharSequence remoteInputText = null;
-                    if (!TextUtils.isEmpty(entry.remoteInputText)) {
-                        remoteInputText = entry.remoteInputText;
-                    }
-                    if (!TextUtils.isEmpty(remoteInputText)
-                            && !controller.isSpinning(entry.key)) {
-                        fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
-                                remoteInputText.toString());
-                    }
-                    RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
-                            row, wasOccluded);
-                    try {
-                        if (adapter != null) {
-                            ActivityTaskManager.getService()
-                                    .registerRemoteAnimationForNextActivityStart(
-                                            intent.getCreatorPackage(), adapter);
-                        }
-                        launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
-                                null, null, getActivityOptions(adapter));
-                        mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
-                    } catch (RemoteException | PendingIntent.CanceledException e) {
-                        // the stack trace isn't very helpful here.
-                        // Just log the exception message.
-                        Log.w(TAG, "Sending contentIntent failed: " + e);
-
-                        // TODO: Dismiss Keyguard.
-                    }
-                    if (isActivityIntent) {
-                        mAssistManager.hideAssist();
-                    }
-                }
-                if (shouldCollapse()) {
-                    collapseOnMainThread();
-                }
-
-                final int count =
-                        mEntryManager.getNotificationData().getActiveNotifications().size();
-                final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
-                final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
-                        rank, count, true);
-                try {
-                    mBarService.onNotificationClick(notificationKey, nv);
-                } catch (RemoteException ex) {
-                    // system process is dead if we're here.
-                }
-                if (parentToCancelFinal != null) {
-                    removeNotification(parentToCancelFinal);
-                }
-                if (shouldAutoCancel(sbn)
-                        || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
-                                notificationKey)) {
-                    // Automatically remove all notifications that we may have kept around longer
-                    removeNotification(sbn);
-                }
-                mIsCollapsingToShowActivityOverLockscreen = false;
-            };
-
-            if (showOverLockscreen) {
-                mShadeController.addPostCollapseAction(runnable);
-                mShadeController.collapsePanel(true /* animate */);
-            } else if (mKeyguardMonitor.isShowing()
-                    && mShadeController.isOccluded()) {
-                mShadeController.addAfterKeyguardGoneRunnable(runnable);
-                mShadeController.collapsePanel();
-            } else {
-                new Thread(runnable).start();
-            }
-
-            return !mNotificationPanel.isFullyCollapsed();
-        };
-        if (showOverLockscreen) {
-            mIsCollapsingToShowActivityOverLockscreen = true;
-            postKeyguardAction.onDismiss();
-        } else {
-            mActivityStarter.dismissKeyguardThenExecute(
-                    postKeyguardAction, null /* cancel */, afterKeyguardGone);
-        }
-    }
-
-    private void removeNotification(StatusBarNotification notification) {
-        // We have to post it to the UI thread for synchronization
-        Dependency.get(MAIN_HANDLER).post(() -> {
-            Runnable removeRunnable =
-                    () -> mEntryManager.performRemoveNotification(notification);
-            if (isCollapsing()) {
-                // To avoid lags we're only performing the remove
-                // after the shade was collapsed
-                mShadeController.addPostCollapseAction(removeRunnable);
-            } else {
-                removeRunnable.run();
-            }
-        });
-    }
-
-    @Override
-    public void startNotificationGutsIntent(final Intent intent, final int appUid,
-            ExpandableNotificationRow row) {
-        mActivityStarter.dismissKeyguardThenExecute(() -> {
-            AsyncTask.execute(() -> {
-                int launchResult = TaskStackBuilder.create(mContext)
-                        .addNextIntentWithParentStack(intent)
-                        .startActivities(getActivityOptions(
-                                mActivityLaunchAnimator.getLaunchAnimation(
-                                        row, mShadeController.isOccluded())),
-                                new UserHandle(UserHandle.getUserId(appUid)));
-                mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
-                if (shouldCollapse()) {
-                    // Putting it back on the main thread, since we're touching views
-                    Dependency.get(MAIN_HANDLER).post(() -> mCommandQueue.animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
-                }
-            });
-            return true;
-        }, null, false /* afterKeyguardGone */);
-    }
-
-    @Override
     public int getMaxNotificationsWhileLocked(boolean recompute) {
         if (recompute) {
             mMaxKeyguardNotifications = Math.max(1,
@@ -600,41 +367,12 @@
         return mVrMode;
     }
 
-    @Override
-    public boolean isPresenterLocked() {
-        return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
-    }
-
-    private void collapseOnMainThread() {
-        if (Looper.getMainLooper().isCurrentThread()) {
-            mShadeController.collapsePanel();
-        } else {
-            Dependency.get(MAIN_HANDLER).post(mShadeController::collapsePanel);
-        }
-    }
-
-    private boolean shouldCollapse() {
-        return mStatusBarStateController.getState() != StatusBarState.SHADE
-                || !mActivityLaunchAnimator.isAnimationPending();
-    }
-
     private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
         mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
         mActivityStarter.dismissKeyguardThenExecute(dismissAction, null,
                 true /* afterKeyguardGone */);
     }
 
-    private static boolean shouldAutoCancel(StatusBarNotification sbn) {
-        int flags = sbn.getNotification().flags;
-        if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
-            return false;
-        }
-        if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-            return false;
-        }
-        return true;
-    }
-
     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
         @Override
         public void onVrStateChanged(boolean enabled) {
@@ -645,7 +383,6 @@
     private final CheckSaveListener mCheckSaveListener = new CheckSaveListener() {
         @Override
         public void checkSave(Runnable saveImportance, StatusBarNotification sbn) {
-            int state = mStatusBarStateController.getState();
             // If the user has security enabled, show challenge if the setting is changed.
             if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
                     && mKeyguardManager.isKeyguardLocked()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a743d41e..78f5374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -17,8 +17,7 @@
 import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
 
 import static com.android.systemui.SysUiServiceProvider.getComponent;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager
-        .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
 
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
@@ -70,10 +69,10 @@
         mContext = context;
         mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
                 new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
-        mStatusBarStateController.addListener(mStateListener);
+        mStatusBarStateController.addCallback(mStateListener);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
         mCommandQueue = getComponent(context, CommandQueue.class);
-        mCommandQueue.addCallbacks(this);
+        mCommandQueue.addCallback(this);
     }
 
     private void setStatusBarState(int state) {
@@ -163,7 +162,7 @@
         mPendingWorkRemoteInputView = clicked;
     }
 
-    protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
+    boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
             String notificationKey) {
         // Clear pending remote view, as we do not want to trigger pending remote input view when
         // it's called by other code
@@ -206,7 +205,7 @@
     }
 
     @Override
-    public boolean handleRemoteViewClick(PendingIntent pendingIntent,
+    public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
             NotificationRemoteInputManager.ClickHandler defaultHandler) {
         final boolean isActivity = pendingIntent.isActivity();
         if (isActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 158ee8a..986a86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -92,7 +92,7 @@
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
         mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
-        Dependency.get(StatusBarStateController.class).addListener(
+        Dependency.get(StatusBarStateController.class).addCallback(
                 mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
index 9042ca6..626eef5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
@@ -1,8 +1,9 @@
 /*
  * 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
+ * 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
  *
@@ -14,7 +15,35 @@
 
 package com.android.systemui.statusbar.policy;
 
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
 public interface CallbackController<T> {
     void addCallback(T listener);
     void removeCallback(T listener);
+
+    /**
+     * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state
+     * and {@link #removeCallback(Object)} when not resumed automatically.
+     */
+    default T observe(LifecycleOwner owner, T listener) {
+        return observe(owner.getLifecycle(), listener);
+    }
+
+    /**
+     * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state
+     * and {@link #removeCallback(Object)} when not resumed automatically.
+     */
+    default T observe(Lifecycle lifecycle, T listener) {
+        lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> {
+            if (event == Event.ON_RESUME) {
+                addCallback(listener);
+            } else if (event == Event.ON_PAUSE) {
+                removeCallback(listener);
+            }
+        });
+        return listener;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index c2af95e..aafdcd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -187,7 +187,7 @@
                     null, Dependency.get(Dependency.TIME_TICK_HANDLER));
             Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
                     StatusBarIconController.ICON_BLACKLIST);
-            SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+            SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallback(this);
             if (mShowDark) {
                 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
             }
@@ -215,7 +215,7 @@
             mAttached = false;
             Dependency.get(TunerService.class).removeTunable(this);
             SysUiServiceProvider.getComponent(getContext(), CommandQueue.class)
-                    .removeCallbacks(this);
+                    .removeCallback(this);
             if (mShowDark) {
                 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
index 639e50c..9c099f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.os.SystemProperties;
+import android.sysprop.VoldProperties;
 
 /**
  * Helper for determining whether the phone is decrypted yet.
@@ -26,7 +26,7 @@
     public static final boolean IS_DATA_ENCRYPTED = isDataEncrypted();
 
     private static boolean isDataEncrypted() {
-        String voldState = SystemProperties.get("vold.decrypt");
+        String voldState = VoldProperties.decrypt().orElse("");
         return "1".equals(voldState) || "trigger_restart_min_framework".equals(voldState);
     }
 }
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 f36066c..2a4336e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -21,7 +21,6 @@
 import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Size;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,6 +40,7 @@
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.PriorityQueue;
@@ -282,9 +282,9 @@
 
         Drawable iconDrawable = action.getIcon().loadDrawable(context);
         // Add the action icon to the Smart Action button.
-        Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
-                new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
-        iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+        int newIconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.smart_action_button_icon_size);
+        iconDrawable.setBounds(0, 0, newIconSize, newIconSize);
         button.setCompoundDrawables(iconDrawable, null, null, null);
 
         button.setOnClickListener(view ->
@@ -295,30 +295,12 @@
 
         // TODO(b/119010281): handle accessibility
 
+        // Mark this as an Action button
+        final LayoutParams lp = (LayoutParams) button.getLayoutParams();
+        lp.buttonType = SmartButtonType.ACTION;
         return button;
     }
 
-    private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
-            Size originalIconSize) {
-        Button button = (Button) LayoutInflater.from(context).inflate(
-                R.layout.smart_action_button, root, false);
-        // Add simple text here to ensure the button displays one line of text.
-        button.setText("a");
-        return calculateIconSizeFromButtonHeight(button, originalIconSize);
-    }
-
-    // Given a button with text on a single line - we want to add an icon to that button. This
-    // method calculates the icon height to use to avoid making the button grow in height.
-    private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
-        // A completely permissive measure spec should make the button text single-line.
-        button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
-        int buttonHeight = button.getMeasuredHeight();
-        int newIconHeight = buttonHeight / 2;
-        int newIconWidth = (int) (originalIconSize.getWidth()
-                * ((double) newIconHeight) / originalIconSize.getHeight());
-        return new Size(newIconWidth, newIconHeight);
-    }
-
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
@@ -352,18 +334,26 @@
         int displayedChildCount = 0;
         int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal;
 
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
+        // Set up a list of suggestions where actions come before replies. Note that the Buttons
+        // themselves have already been added to the view hierarchy in an order such that Smart
+        // Replies are shown before Smart Actions. The order of the list below determines which
+        // suggestions will be shown at all - only the first X elements are shown (where X depends
+        // on how much space each suggestion button needs).
+        List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION);
+        List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY);
+        List<View> smartSuggestions = new ArrayList<>(smartActions);
+        smartSuggestions.addAll(smartReplies);
+        List<View> coveredSuggestions = new ArrayList<>();
+
+        for (View child : smartSuggestions) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
-                continue;
-            }
 
             child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
                     buttonPaddingHorizontal, child.getPaddingBottom());
             child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
 
+            coveredSuggestions.add(child);
+
             final int lineCount = ((Button) child).getLineCount();
             if (lineCount < 1 || lineCount > 2) {
                 // If smart reply has no text, or more than two lines, then don't show it.
@@ -417,7 +407,8 @@
 
                     // Mark all buttons from the last squeezing round as "failed to squeeze", so
                     // that they're re-measured without squeezing later.
-                    markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_FAILED, i);
+                    markButtonsWithPendingSqueezeStatusAs(
+                            LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
 
                     // The current button doesn't fit, so there's no point in measuring further
                     // buttons.
@@ -426,7 +417,8 @@
 
                 // The current button fits, so mark all squeezed buttons as "successfully squeezed"
                 // to prevent them from being un-squeezed in a subsequent squeezing round.
-                markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, i);
+                markButtonsWithPendingSqueezeStatusAs(
+                        LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, coveredSuggestions);
             }
 
             lp.show = true;
@@ -445,6 +437,22 @@
                         mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec));
     }
 
+    private List<View> filterActionsOrReplies(SmartButtonType buttonType) {
+        List<View> actions = new ArrayList<>();
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
+                continue;
+            }
+            if (lp.buttonType == buttonType) {
+                actions.add(child);
+            }
+        }
+        return actions;
+    }
+
     private void resetButtonsLayoutParams() {
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -615,9 +623,9 @@
         }
     }
 
-    private void markButtonsWithPendingSqueezeStatusAs(int squeezeStatus, int maxChildIndex) {
-        for (int i = 0; i <= maxChildIndex; i++) {
-            final View child = getChildAt(i);
+    private void markButtonsWithPendingSqueezeStatusAs(
+            int squeezeStatus, List<View> coveredChildren) {
+        for (View child : coveredChildren) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.squeezeStatus == LayoutParams.SQUEEZE_STATUS_PENDING) {
                 lp.squeezeStatus = squeezeStatus;
@@ -712,6 +720,11 @@
         return mActivityStarter;
     }
 
+    private enum SmartButtonType {
+        REPLY,
+        ACTION
+    }
+
     @VisibleForTesting
     static class LayoutParams extends ViewGroup.LayoutParams {
 
@@ -737,6 +750,7 @@
 
         private boolean show = false;
         private int squeezeStatus = SQUEEZE_STATUS_NONE;
+        private SmartButtonType buttonType = SmartButtonType.REPLY;
 
         private LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index cd379c5..4a69cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -42,7 +42,7 @@
     public void start() {
         putComponent(TvStatusBar.class, this);
         CommandQueue commandQueue = getComponent(CommandQueue.class);
-        commandQueue.addCallbacks(this);
+        commandQueue.addCallback(this);
         int[] switches = new int[9];
         ArrayList<IBinder> binders = new ArrayList<>();
         ArrayList<String> iconSlots = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index 0f7c9a4..096ac3f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -18,19 +18,24 @@
 
 import android.os.Looper;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Helper providing common assertions.
  */
 public class Assert {
 
+    @VisibleForTesting
+    public static Looper sMainLooper = Looper.getMainLooper();
+
     public static void isMainThread() {
-        if (!Looper.getMainLooper().isCurrentThread()) {
+        if (!sMainLooper.isCurrentThread()) {
             throw new IllegalStateException("should be called from the main thread.");
         }
     }
 
     public static void isNotMainThread() {
-        if (Looper.getMainLooper().isCurrentThread()) {
+        if (sMainLooper.isCurrentThread()) {
             throw new IllegalStateException("should not be called from the main thread.");
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java b/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java
new file mode 100644
index 0000000..c7f385d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java
@@ -0,0 +1,87 @@
+/*
+ * 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.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.annotation.CallSuper;
+import android.app.Fragment;
+import android.os.Bundle;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+/**
+ * Version of {@link #Fragment} that is a {@link LifecycleOwner}.
+ */
+public class LifecycleFragment extends Fragment implements LifecycleOwner {
+
+    private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+    public Lifecycle getLifecycle() {
+        return mLifecycle;
+    }
+
+    @CallSuper
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        mLifecycle.handleLifecycleEvent(ON_CREATE);
+        super.onCreate(savedInstanceState);
+    }
+
+    @CallSuper
+    @Override
+    public void onStart() {
+        mLifecycle.handleLifecycleEvent(ON_START);
+        super.onStart();
+    }
+
+    @CallSuper
+    @Override
+    public void onResume() {
+        mLifecycle.handleLifecycleEvent(ON_RESUME);
+        super.onResume();
+    }
+
+    @CallSuper
+    @Override
+    public void onPause() {
+        mLifecycle.handleLifecycleEvent(ON_PAUSE);
+        super.onPause();
+    }
+
+    @CallSuper
+    @Override
+    public void onStop() {
+        mLifecycle.handleLifecycleEvent(ON_STOP);
+        super.onStop();
+    }
+
+    @CallSuper
+    @Override
+    public void onDestroy() {
+        mLifecycle.handleLifecycleEvent(ON_DESTROY);
+        super.onDestroy();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
new file mode 100644
index 0000000..711a0df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
@@ -0,0 +1,69 @@
+/*
+ * 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.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.State.DESTROYED;
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+/**
+ * Tools for generating lifecycle from sysui objects.
+ */
+public class SysuiLifecycle {
+
+    private SysuiLifecycle() {
+    }
+
+    /**
+     * Get a lifecycle that will be put into the resumed state when the view is attached
+     * and goes to the destroyed state when the view is detached.
+     */
+    public static LifecycleOwner viewAttachLifecycle(View v) {
+        return new ViewLifecycle(v);
+    }
+
+    private static class ViewLifecycle implements LifecycleOwner, OnAttachStateChangeListener {
+        private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+        ViewLifecycle(View v) {
+            v.addOnAttachStateChangeListener(this);
+        }
+
+        @NonNull
+        @Override
+        public Lifecycle getLifecycle() {
+            return mLifecycle;
+        }
+
+        @Override
+        public void onViewAttachedToWindow(View v) {
+            mLifecycle.markState(RESUMED);
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            mLifecycle.markState(DESTROYED);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 6812410..490cdd5c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -57,13 +57,13 @@
         public void onViewAttachedToWindow(View v) {
             mView = v;
             SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class)
-                    .addCallbacks(this);
+                    .addCallback(this);
         }
 
         @Override
         public void onViewDetachedFromWindow(View v) {
             SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class)
-                    .removeCallbacks(this);
+                    .removeCallback(this);
             mView = null;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
deleted file mode 100644
index 9b616e0..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ /dev/null
@@ -1,605 +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.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
-import android.app.Dialog;
-import android.app.KeyguardManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.media.CarAudioManager;
-import android.car.media.ICarVolumeCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.ServiceConnection;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-
-import androidx.car.widget.ListItem;
-import androidx.car.widget.ListItemAdapter;
-import androidx.car.widget.ListItemAdapter.BackgroundStyle;
-import androidx.car.widget.ListItemProvider.ListProvider;
-import androidx.car.widget.PagedListView;
-import androidx.car.widget.SeekbarListItem;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.VolumeDialog;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Car version of the volume dialog.
- *
- * Methods ending in "H" must be called on the (ui) handler.
- */
-public class CarVolumeDialogImpl implements VolumeDialog {
-  private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
-
-  private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
-  private static final String XML_TAG_VOLUME_ITEM = "item";
-  private static final int HOVERING_TIMEOUT = 16000;
-  private static final int NORMAL_TIMEOUT = 3000;
-  private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
-  private static final int DISMISS_DELAY_IN_MILLIS = 50;
-  private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
-
-  private final Context mContext;
-  private final H mHandler = new H();
-
-  private Window mWindow;
-  private CustomDialog mDialog;
-  private PagedListView mListView;
-  private ListItemAdapter mPagedListAdapter;
-  // All the volume items.
-  private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
-  // Available volume items in car audio manager.
-  private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
-  // Volume items in the PagedListView.
-  private final List<ListItem> mVolumeLineItems = new ArrayList<>();
-  private final KeyguardManager mKeyguard;
-
-  private Car mCar;
-  private CarAudioManager mCarAudioManager;
-
-  private boolean mHovering;
-  private boolean mShowing;
-  private boolean mExpanded;
-
-  public CarVolumeDialogImpl(Context context) {
-    mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
-    mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-    mCar = Car.createCar(mContext, mServiceConnection);
-  }
-
-  public void init(int windowType, Callback callback) {
-    initDialog();
-
-    mCar.connect();
-  }
-
-  @Override
-  public void destroy() {
-    mHandler.removeCallbacksAndMessages(null);
-
-    cleanupAudioManager();
-    // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
-    // audio manager beforehand.
-    mCar.disconnect();
-  }
-
-  private void initDialog() {
-    loadAudioUsageItems();
-    mVolumeLineItems.clear();
-    mDialog = new CustomDialog(mContext);
-
-    mHovering = false;
-    mShowing = false;
-    mExpanded = false;
-    mWindow = mDialog.getWindow();
-    mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-    mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-    mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
-        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
-    mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
-    mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-    mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
-    final WindowManager.LayoutParams lp = mWindow.getAttributes();
-    lp.format = PixelFormat.TRANSLUCENT;
-    lp.setTitle(VolumeDialogImpl.class.getSimpleName());
-    lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
-    lp.windowAnimations = -1;
-    mWindow.setAttributes(lp);
-    mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-    mDialog.setCanceledOnTouchOutside(true);
-    mDialog.setContentView(R.layout.car_volume_dialog);
-    mDialog.setOnShowListener(dialog -> {
-      mListView.setTranslationY(-mListView.getHeight());
-      mListView.setAlpha(0);
-      mListView.animate()
-          .alpha(1)
-          .translationY(0)
-          .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
-          .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
-          .start();
-    });
-    mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
-    mListView.setOnHoverListener((v, event) -> {
-      int action = event.getActionMasked();
-      mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
-          || (action == MotionEvent.ACTION_HOVER_MOVE);
-      rescheduleTimeoutH();
-      return true;
-    });
-
-    mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
-        BackgroundStyle.PANEL);
-    mListView.setAdapter(mPagedListAdapter);
-    mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
-  }
-
-  public void show(int reason) {
-    mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
-  }
-
-  public void dismiss(int reason) {
-    mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
-  }
-
-  private void showH(int reason) {
-    if (D.BUG) {
-      Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
-    }
-
-    mHandler.removeMessages(H.SHOW);
-    mHandler.removeMessages(H.DISMISS);
-    rescheduleTimeoutH();
-    // Refresh the data set before showing.
-    mPagedListAdapter.notifyDataSetChanged();
-    if (mShowing) {
-      return;
-    }
-    mShowing = true;
-
-    mDialog.show();
-    Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
-  }
-
-  protected void rescheduleTimeoutH() {
-    mHandler.removeMessages(H.DISMISS);
-    final int timeout = computeTimeoutH();
-    mHandler.sendMessageDelayed(mHandler
-        .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
-
-    if (D.BUG) {
-      Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
-    }
-  }
-
-  private int computeTimeoutH() {
-    return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
-  }
-
-  protected void dismissH(int reason) {
-    if (D.BUG) {
-      Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
-    }
-
-    mHandler.removeMessages(H.DISMISS);
-    mHandler.removeMessages(H.SHOW);
-    if (!mShowing) {
-      return;
-    }
-
-    mListView.animate().cancel();
-
-    mListView.setTranslationY(0);
-    mListView.setAlpha(1);
-    mListView.animate()
-        .alpha(0)
-        .translationY(-mListView.getHeight())
-        .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
-        .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
-        .withEndAction(() -> mHandler.postDelayed(() -> {
-          if (D.BUG) {
-            Log.d(TAG, "mDialog.dismiss()");
-          }
-          mDialog.dismiss();
-          mShowing = false;
-        }, DISMISS_DELAY_IN_MILLIS))
-        .start();
-
-    Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
-  }
-
-  public void dump(PrintWriter writer) {
-    writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
-    writer.print("  mShowing: "); writer.println(mShowing);
-  }
-
-  private void loadAudioUsageItems() {
-    try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
-      AttributeSet attrs = Xml.asAttributeSet(parser);
-      int type;
-      // Traverse to the first start tag
-      while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
-          && type != XmlResourceParser.START_TAG) {
-      }
-
-      if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
-        throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
-      }
-      int outerDepth = parser.getDepth();
-      int rank = 0;
-      while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
-          && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
-        if (type == XmlResourceParser.END_TAG) {
-          continue;
-        }
-        if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
-          TypedArray item = mContext.getResources().obtainAttributes(
-              attrs, R.styleable.carVolumeItems_item);
-          int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
-          if (usage >= 0) {
-            VolumeItem volumeItem = new VolumeItem();
-            volumeItem.usage = usage;
-            volumeItem.rank = rank;
-            volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0);
-            mVolumeItems.put(usage, volumeItem);
-            rank++;
-          }
-          item.recycle();
-        }
-      }
-    } catch (XmlPullParserException | IOException e) {
-      Log.e(TAG, "Error parsing volume groups configuration", e);
-    }
-  }
-
-  private VolumeItem getVolumeItemForUsages(int[] usages) {
-    int rank = Integer.MAX_VALUE;
-    VolumeItem result = null;
-    for (int usage : usages) {
-      VolumeItem volumeItem = mVolumeItems.get(usage);
-      if (volumeItem.rank < rank) {
-        rank = volumeItem.rank;
-        result = volumeItem;
-      }
-    }
-    return result;
-  }
-
-  private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
-    try {
-      return carAudioManager.getGroupVolume(volumeGroupId);
-    } catch (CarNotConnectedException e) {
-      Log.e(TAG, "Car is not connected!", e);
-    }
-    return 0;
-  }
-
-  private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
-    try {
-      return carAudioManager.getGroupMaxVolume(volumeGroupId);
-    } catch (CarNotConnectedException e) {
-      Log.e(TAG, "Car is not connected!", e);
-    }
-    return 0;
-  }
-
-  private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId,
-      int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) {
-    SeekbarListItem listItem = new SeekbarListItem(mContext);
-    listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
-    int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
-    int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
-    listItem.setProgress(progress);
-    listItem.setOnSeekBarChangeListener(
-        new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
-    Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
-    primaryIcon.mutate().setTint(color);
-    listItem.setPrimaryActionIcon(primaryIcon);
-    if (supplementalIconId != 0) {
-      Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
-      supplementalIcon.mutate().setTint(color);
-      listItem.setSupplementalIcon(supplementalIcon, true);
-      listItem.setSupplementalIconListener(supplementalIconOnClickListener);
-    } else {
-      listItem.setSupplementalEmptyIcon(true);
-      listItem.setSupplementalIconListener(null);
-    }
-
-    mVolumeLineItems.add(listItem);
-    volumeItem.listItem = listItem;
-    volumeItem.progress = progress;
-    return listItem;
-  }
-
-  private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
-    for (int i = 0; i < mVolumeItems.size(); ++i) {
-      VolumeItem volumeItem = mVolumeItems.valueAt(i);
-      if (volumeItem.listItem == targetItem) {
-        return volumeItem;
-      }
-    }
-    return null;
-  }
-
-  private void cleanupAudioManager() {
-    try {
-      mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
-    } catch (CarNotConnectedException e) {
-      Log.e(TAG, "Car is not connected!", e);
-    }
-    mVolumeLineItems.clear();
-    mCarAudioManager = null;
-  }
-
-  private final class H extends Handler {
-    private static final int SHOW = 1;
-    private static final int DISMISS = 2;
-
-    public H() {
-      super(Looper.getMainLooper());
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-      switch (msg.what) {
-        case SHOW:
-          showH(msg.arg1);
-          break;
-        case DISMISS:
-          dismissH(msg.arg1);
-          break;
-        default:
-      }
-    }
-  }
-
-  private final class CustomDialog extends Dialog implements DialogInterface {
-    public CustomDialog(Context context) {
-      super(context, com.android.systemui.R.style.qs_theme);
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-      rescheduleTimeoutH();
-      return super.dispatchTouchEvent(ev);
-    }
-
-    @Override
-    protected void onStart() {
-      super.setCanceledOnTouchOutside(true);
-      super.onStart();
-    }
-
-    @Override
-    protected void onStop() {
-      super.onStop();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-      if (isShowing()) {
-        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-          mHandler.obtainMessage(
-            H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
-          return true;
-        }
-      }
-      return false;
-    }
-  }
-
-  private final class ExpandIconListener implements View.OnClickListener {
-    @Override
-    public void onClick(final View v) {
-      mExpanded = !mExpanded;
-      Animator inAnimator;
-      if (mExpanded) {
-        for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
-          // Adding the items which are not coming from the default item.
-          VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-          if (volumeItem.defaultItem) {
-            // Set progress here due to the progress of seekbar may not be updated.
-            volumeItem.listItem.setProgress(volumeItem.progress);
-          } else {
-            addSeekbarListItem(volumeItem, groupId, 0, null);
-          }
-        }
-        inAnimator = AnimatorInflater.loadAnimator(
-            mContext, R.anim.car_arrow_fade_in_rotate_up);
-      } else {
-        // Only keeping the default stream if it is not expended.
-        Iterator itr = mVolumeLineItems.iterator();
-        while (itr.hasNext()) {
-          SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
-          VolumeItem volumeItem = findVolumeItem(seekbarListItem);
-          if (!volumeItem.defaultItem) {
-            itr.remove();
-          } else {
-            // Set progress here due to the progress of seekbar may not be updated.
-            seekbarListItem.setProgress(volumeItem.progress);
-          }
-        }
-        inAnimator = AnimatorInflater.loadAnimator(
-            mContext, R.anim.car_arrow_fade_in_rotate_down);
-      }
-
-      Animator outAnimator = AnimatorInflater.loadAnimator(
-          mContext, R.anim.car_arrow_fade_out);
-      inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
-      AnimatorSet animators = new AnimatorSet();
-      animators.playTogether(outAnimator, inAnimator);
-      animators.setTarget(v);
-      animators.start();
-      mPagedListAdapter.notifyDataSetChanged();
-    }
-  }
-
-  private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
-    private final int mVolumeGroupId;
-    private final CarAudioManager mCarAudioManager;
-
-    private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
-      mVolumeGroupId = volumeGroupId;
-      mCarAudioManager = carAudioManager;
-    }
-
-    @Override
-    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-      if (!fromUser) {
-        // For instance, if this event is originated from AudioService,
-        // we can ignore it as it has already been handled and doesn't need to be
-        // sent back down again.
-        return;
-      }
-      try {
-        if (mCarAudioManager == null) {
-          Log.w(TAG, "Ignoring volume change event because the car isn't connected");
-          return;
-        }
-        mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
-        mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
-      } catch (CarNotConnectedException e) {
-        Log.e(TAG, "Car is not connected!", e);
-      }
-    }
-
-    @Override
-    public void onStartTrackingTouch(SeekBar seekBar) {}
-
-    @Override
-    public void onStopTrackingTouch(SeekBar seekBar) {}
-  }
-
-  private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
-    @Override
-    public void onGroupVolumeChanged(int groupId, int flags) {
-      VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-      int value = getSeekbarValue(mCarAudioManager, groupId);
-      // Do not update the progress if it is the same as before. When car audio manager sets its
-      // group volume caused by the seekbar progress changed, it also triggers this callback.
-      // Updating the seekbar at the same time could block the continuous seeking.
-      if (value != volumeItem.progress) {
-        volumeItem.listItem.setProgress(value);
-        volumeItem.progress = value;
-      }
-      if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
-        show(Events.SHOW_REASON_VOLUME_CHANGED);
-      }
-    }
-
-    @Override
-    public void onMasterMuteChanged(int flags) {
-      // ignored
-    }
-  };
-
-  private final ServiceConnection mServiceConnection = new ServiceConnection() {
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-      try {
-        mExpanded = false;
-        mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
-        int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
-        // Populates volume slider items from volume groups to UI.
-        for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
-          VolumeItem volumeItem = getVolumeItemForUsages(
-              mCarAudioManager.getUsagesForVolumeGroupId(groupId));
-          mAvailableVolumeItems.add(volumeItem);
-          // The first one is the default item.
-          if (groupId == 0) {
-            volumeItem.defaultItem = true;
-            addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down,
-                new ExpandIconListener());
-          }
-        }
-
-        // If list is already initiated, update its content.
-        if (mPagedListAdapter != null) {
-          mPagedListAdapter.notifyDataSetChanged();
-        }
-        mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
-      } catch (CarNotConnectedException e) {
-        Log.e(TAG, "Car is not connected!", e);
-      }
-    }
-
-    /**
-     * This does not get called when service is properly disconnected.
-     * So we need to also handle cleanups in destroy().
-     */
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
-      cleanupAudioManager();
-    }
-  };
-
-  /**
-   * Wrapper class which contains information of each volume group.
-   */
-  private static class VolumeItem {
-    private @AudioAttributes.AttributeUsage int usage;
-    private int rank;
-    private boolean defaultItem = false;
-    private @DrawableRes int icon;
-    private SeekbarListItem listItem;
-    private int progress;
-  }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 2861dff..0805677 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -19,12 +19,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.VolumePolicy;
 import android.os.Bundle;
-import android.os.Handler;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
@@ -57,7 +55,7 @@
     public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
 
     private final SystemUI mSysui;
-    private final Context mContext;
+    protected final Context mContext;
     private final VolumeDialogControllerImpl mController;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
@@ -70,7 +68,7 @@
             400    // vibrateToSilentDebounce
     );
 
-    public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
+    public VolumeDialogComponent(SystemUI sysui, Context context) {
         mSysui = sysui;
         mContext = context;
         mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
@@ -81,7 +79,6 @@
         Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                 .withPlugin(VolumeDialog.class)
                 .withDefault(this::createDefault)
-                .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
                 .withCallback(dialog -> {
                     if (mDialog != null) {
                         mDialog.destroy();
@@ -94,7 +91,7 @@
                 VOLUME_SILENT_DO_NOT_DISTURB);
     }
 
-    private VolumeDialog createDefault() {
+    protected VolumeDialog createDefault() {
         VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
         impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
         impl.setAutomute(true);
@@ -102,10 +99,6 @@
         return impl;
     }
 
-    private VolumeDialog createCarDefault() {
-        return new CarVolumeDialogImpl(mContext);
-    }
-
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (VOLUME_DOWN_SILENT.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e4f37de..f8cf793 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,6 +22,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.qs.tiles.DndTile;
 
 import java.io.FileDescriptor;
@@ -43,7 +44,9 @@
             mContext.getResources().getBoolean(R.bool.enable_safety_warning);
         mEnabled = enableVolumeUi || enableSafetyWarning;
         if (!mEnabled) return;
-        mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
+
+        mVolumeComponent = SystemUIFactory.getInstance()
+                .createVolumeDialogComponent(this, mContext);
         mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
         putComponent(VolumeComponent.class, getVolumeComponent());
         setDefaultVolumeController();
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index b32bf99..83ec33c 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -38,8 +38,6 @@
     android.test.runner \
     telephony-common \
     android.test.base \
-    android.car \
-    android.car.userlib
 
 LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 9cbe415..7ca5423 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -51,7 +51,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardClockSwitchTest extends SysuiTestCase {
     private PluginManager mPluginManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
index 359832f..58870e4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
@@ -38,7 +38,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index b98ce39..77895c9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -19,9 +19,14 @@
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.LayoutInflater;
 
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 
@@ -34,12 +39,8 @@
 import java.util.HashSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-
 @SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardSliceViewTest extends SysuiTestCase {
     private KeyguardSliceView mKeyguardSliceView;
@@ -47,6 +48,7 @@
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mKeyguardSliceView = (KeyguardSliceView) LayoutInflater.from(getContext())
                 .inflate(R.layout.keyguard_status_area, null);
         mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 9e96df2..3582ab0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -20,10 +20,12 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.LayoutInflater;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -32,7 +34,7 @@
 import org.mockito.Mock;
 
 @SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardStatusViewTest extends SysuiTestCase {
 
@@ -45,6 +47,7 @@
 
     @Before
     public void setUp() {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         mKeyguardStatusView =
                 (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index c180ff8..948e001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -16,29 +16,29 @@
 
 package com.android.systemui;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.animation.ObjectAnimator;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.util.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class ExpandHelperTest extends SysuiTestCase {
 
     private ExpandableNotificationRow mRow;
@@ -47,6 +47,7 @@
 
     @Before
     public void setUp() throws Exception {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         Context context = getContext();
         mRow = new NotificationTestHelper(context).createRow();
         mCallback = mock(ExpandHelper.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index a58bc85..9c5a592 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.util.Assert;
 
 import org.junit.After;
 import org.junit.Before;
@@ -78,6 +79,8 @@
     public void SysuiTeardown() {
         InstrumentationRegistry.registerInstance(mRealInstrumentation,
                 InstrumentationRegistry.getArguments());
+        // Reset the assert's main looper.
+        Assert.sMainLooper = Looper.getMainLooper();
     }
 
     protected LeakCheck getLeakCheck() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 368c814f..e1c481e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -28,8 +28,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -40,14 +40,12 @@
 
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
-import android.view.Display;
+import android.testing.UiThreadTest;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
-import android.testing.UiThreadTest;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 5c8336c..31fc625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -48,7 +48,7 @@
 @SmallTest
 @Ignore("failing")
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class DozeTriggersTest extends SysuiTestCase {
     private DozeTriggers mTriggers;
     private DozeMachine mMachine;
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 0953951..c1c80ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -55,7 +55,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class KeyguardSliceProviderTest extends SysuiTestCase {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 4e24354..bc7d983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -18,39 +18,32 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
-import android.app.FragmentController;
-import android.app.FragmentManagerNonConfig;
+import android.content.Context;
 import android.os.Looper;
-import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-
-import android.os.Parcelable;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
-import android.testing.LayoutInflaterBuilder;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import android.content.Context;
-import android.view.View;
-import android.widget.FrameLayout;
-
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 @SmallTest
 @Ignore
 public class QSFragmentTest extends SysuiBaseFragmentTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 33b347a..fd31013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -22,7 +22,6 @@
 
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.os.Looper;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -58,7 +57,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class QSSecurityFooterTest extends SysuiTestCase {
 
     private final String MANAGING_ORGANIZATION = "organization";
@@ -76,7 +75,8 @@
     @Before
     public void setUp() {
         mDependency.injectTestDependency(SecurityController.class, mSecurityController);
-        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
                 new LayoutInflaterBuilder(mContext)
                         .replace("ImageView", TestableImageView.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
new file mode 100644
index 0000000..78700b8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.qs;
+
+
+import static junit.framework.TestCase.assertFalse;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class QSTileHostTest extends SysuiTestCase {
+
+    @Test
+    public void testLoadTileSpecs_emptySetting() {
+        List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
+        assertFalse(tiles.isEmpty());
+    }
+
+    @Test
+    public void testLoadTileSpecs_nullSetting() {
+        List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
+        assertFalse(tiles.isEmpty());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
deleted file mode 100644
index f89a932..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.systemui.qs.car;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.LayoutInflaterBuilder;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.keyguard.CarrierText;
-import com.android.systemui.Dependency;
-import com.android.systemui.SysuiBaseFragmentTest;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.Clock;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link CarQSFragment}.
- */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-@SmallTest
-@Ignore
-public class CarQsFragmentTest extends SysuiBaseFragmentTest {
-    public CarQsFragmentTest() {
-        super(CarQSFragment.class);
-    }
-
-    @Before
-    public void initDependencies() {
-        mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
-                new LayoutInflaterBuilder(mContext)
-                        .replace("com.android.systemui.statusbar.policy.SplitClockView",
-                                FrameLayout.class)
-                        .replace("TextClock", View.class)
-                        .replace(CarrierText.class, View.class)
-                        .replace(Clock.class, View.class)
-                        .build());
-        mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mDependency.injectTestDependency(Dependency.BG_LOOPER,
-                TestableLooper.get(this).getLooper());
-    }
-
-    @Test
-    @Ignore("Flaky")
-    public void testLayoutInflation() {
-        CarQSFragment fragment = (CarQSFragment) mFragment;
-        mFragments.dispatchResume();
-
-        assertNotNull(fragment.getHeader());
-        assertNotNull(fragment.getFooter());
-    }
-
-    @Test
-    @Ignore("Flaky")
-    public void testListening() {
-        CarQSFragment qs = (CarQSFragment) mFragment;
-        mFragments.dispatchResume();
-        processAllMessages();
-
-        qs.setListening(true);
-        processAllMessages();
-
-        qs.setListening(false);
-        processAllMessages();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index c016a85..c6597b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -24,16 +24,15 @@
 import android.os.Looper;
 import android.service.quicksettings.Tile;
 import android.test.suitebuilder.annotation.SmallTest;
-
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -45,7 +44,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class TileServicesTest extends SysuiTestCase {
     private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 8ae3cd8..a04e57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -42,7 +44,7 @@
     public void setup() {
         mCommandQueue = new CommandQueue();
         mCallbacks = mock(Callbacks.class);
-        mCommandQueue.addCallbacks(mCallbacks);
+        mCommandQueue.addCallback(mCallbacks);
         verify(mCallbacks).disable(eq(0), eq(0), eq(false));
     }
 
@@ -64,11 +66,12 @@
         verify(mCallbacks).removeIcon(eq(slot));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testDisable() {
         int state1 = 14;
         int state2 = 42;
-        mCommandQueue.disable(state1, state2);
+        mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
         waitForIdleSync();
         verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
     }
@@ -95,24 +98,27 @@
         verify(mCallbacks).animateExpandSettingsPanel(eq(panel));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testSetSystemUiVisibility() {
         Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(1, 2, 3, 4, null, r);
+        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
         waitForIdleSync();
         verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testTopAppWindowChanged() {
-        mCommandQueue.topAppWindowChanged(true);
+        mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
         waitForIdleSync();
         verify(mCallbacks).topAppWindowChanged(eq(true));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testShowImeButton() {
-        mCommandQueue.setImeWindowStatus(null, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
         waitForIdleSync();
         verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
     }
@@ -166,9 +172,10 @@
         verify(mCallbacks).toggleKeyboardShortcutsMenu(eq(1));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testSetWindowState() {
-        mCommandQueue.setWindowState(1, 2);
+        mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
         verify(mCallbacks).setWindowState(eq(1), eq(2));
     }
@@ -180,30 +187,34 @@
         verify(mCallbacks).showScreenPinningRequest(eq(1));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionPending() {
-        mCommandQueue.appTransitionPending();
+        mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).appTransitionPending(eq(false));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionCancelled() {
-        mCommandQueue.appTransitionCancelled();
+        mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).appTransitionCancelled();
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionStarting() {
-        mCommandQueue.appTransitionStarting(1, 2);
+        mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
         verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionFinished() {
-        mCommandQueue.appTransitionFinished();
+        mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).appTransitionFinished();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 2e280d3..f8ff583 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -50,7 +50,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 @Ignore("b/118400112")
 public class NonPhoneDependencyTest extends SysuiTestCase {
     @Mock private NotificationPresenter mPresenter;
@@ -91,7 +91,7 @@
                 mCheckSaveListener, mOnSettingsClickListener);
         notificationLogger.setUpWithContainer(mListContainer);
         mediaManager.setUpWithPresenter(mPresenter);
-        remoteInputManager.setUpWithPresenter(mPresenter, mRemoteInputManagerCallback,
+        remoteInputManager.setUpWithCallback(mRemoteInputManagerCallback,
                 mDelegate);
         lockscreenUserManager.setUpWithPresenter(mPresenter);
         viewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 63ececb..65c04fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -16,15 +16,12 @@
 
 package com.android.systemui.statusbar;
 
-import static junit.framework.Assert.assertTrue;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -45,7 +42,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class NotificationListenerTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index b5d305d..bee931f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -1,8 +1,8 @@
 package com.android.systemui.statusbar;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -22,13 +22,12 @@
 
 import com.android.systemui.Dependency;
 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 com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import com.google.android.collect.Sets;
 
@@ -80,7 +79,7 @@
         mEntry = new NotificationData.Entry(mSbn);
         mEntry.setRow(mRow);
 
-        mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
+        mRemoteInputManager.setUpWithPresenterForTest(mCallback,
                 mDelegate, mController);
         for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) {
             extender.setCallback(
@@ -201,11 +200,10 @@
             super(context);
         }
 
-        public void setUpWithPresenterForTest(NotificationPresenter presenter,
-                Callback callback,
+        public void setUpWithPresenterForTest(Callback callback,
                 RemoteInputController.Delegate delegate,
                 RemoteInputController controller) {
-            super.setUpWithPresenter(presenter, callback, delegate);
+            super.setUpWithCallback(callback, delegate);
             mRemoteInputController = controller;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 72d6cd8..520a927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.util.Assert;
 
 import com.google.android.collect.Lists;
 
@@ -60,7 +61,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationData mNotificationData;
@@ -79,6 +80,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
                 mLockscreenUserManager);
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 76b992f..8d52ccd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -76,7 +76,7 @@
                 mSmartReplyController);
 
         mRemoteInputManager = new NotificationRemoteInputManager(mContext);
-        mRemoteInputManager.setUpWithPresenter(mPresenter, mCallback, mDelegate);
+        mRemoteInputManager.setUpWithCallback(mCallback, mDelegate);
         mNotification = new Notification.Builder(mContext, "")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index b7aa21b..db2c878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -19,15 +19,15 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.widget.FrameLayout;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -36,7 +36,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class AboveShelfObserverTest extends SysuiTestCase {
 
     private AboveShelfObserver mObserver;
@@ -46,6 +46,7 @@
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mNotificationTestHelper = new NotificationTestHelper(getContext());
         mHostLayout = new FrameLayout(getContext());
         mObserver = new AboveShelfObserver(mHostLayout);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index f94ba95..8e88ed0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -53,6 +53,7 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.ArraySet;
 
@@ -78,7 +79,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class NotificationDataTest extends SysuiTestCase {
 
     private static final int UID_NORMAL = 123;
@@ -101,6 +102,7 @@
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
index 63d1e8d..24aa772 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -17,28 +17,29 @@
 package com.android.systemui.statusbar.notification;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.util.Assert;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class NotificationViewWrapperTest extends SysuiTestCase {
 
     @Test
     public void constructor_doesntUseViewContext() throws Exception {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         new TestableNotificationViewWrapper(mContext,
                 new View(mContext),
                 new NotificationTestHelper(getContext()).createRow());
@@ -50,4 +51,4 @@
             super(ctx, view, row);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 3710fa8..512acd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -37,13 +37,13 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.SysuiTestCase;
-
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+
 import com.google.android.collect.Lists;
 
 import org.junit.Before;
@@ -58,7 +58,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class NotificationLoggerTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2da72e7..6d35539 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -40,6 +40,7 @@
 import android.app.NotificationChannel;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.ArraySet;
 import android.view.NotificationHeaderView;
@@ -64,7 +65,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class ExpandableNotificationRowTest extends SysuiTestCase {
 
     private ExpandableNotificationRow mGroupRow;
@@ -77,6 +78,7 @@
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mNotificationTestHelper = new NotificationTestHelper(mContext);
         mGroupRow = mNotificationTestHelper.createGroup();
         mGroupRow.setHeadsUpAnimatingAwayListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 4efab53..669b98e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -16,26 +16,10 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import android.content.Context;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
 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_POSITIVE;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -45,13 +29,31 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.util.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 /**
  * Tests for {@link NotificationBlockingHelperManager}.
  */
 @SmallTest
 @FlakyTest
 @org.junit.runner.RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
 
     private NotificationBlockingHelperManager mBlockingHelperManager;
@@ -65,6 +67,7 @@
 
     @Before
     public void setUp() {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         when(mGutsManager.openGuts(
                 any(View.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 766c5d2..ad43bea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -59,11 +59,12 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.Assert;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -79,7 +80,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class NotificationGutsManagerTest extends SysuiTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
 
@@ -92,7 +93,7 @@
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
     @Mock private NotificationPresenter mPresenter;
-    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationActivityStarter mNotificationActivityStarter;
     @Mock private NotificationStackScrollLayout mStackScroller;
     @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
     @Mock private OnSettingsClickListener mOnSettingsClickListener;
@@ -101,6 +102,7 @@
     @Before
     public void setUp() {
         mTestableLooper = TestableLooper.get(this);
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mDependency.injectTestDependency(DeviceProvisionedController.class,
                 mDeviceProvisionedController);
         mHandler = Handler.createAsync(mTestableLooper.getLooper());
@@ -110,6 +112,7 @@
         mGutsManager = new NotificationGutsManager(mContext);
         mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
                 mCheckSaveListener, mOnSettingsClickListener);
+        mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
     }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
@@ -217,7 +220,8 @@
         ops.add(OP_CAMERA);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
     }
 
@@ -227,7 +231,8 @@
         ops.add(OP_RECORD_AUDIO);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
     }
 
@@ -238,7 +243,8 @@
         ops.add(OP_RECORD_AUDIO);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
     }
 
@@ -248,7 +254,8 @@
         ops.add(OP_SYSTEM_ALERT_WINDOW);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, captor.getValue().getAction());
     }
 
@@ -260,7 +267,8 @@
         ops.add(OP_SYSTEM_ALERT_WINDOW);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
     }
 
@@ -271,7 +279,8 @@
         ops.add(OP_SYSTEM_ALERT_WINDOW);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
     }
 
@@ -282,7 +291,8 @@
         ops.add(OP_SYSTEM_ALERT_WINDOW);
         mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPresenter, times(1)).startNotificationGutsIntent(captor.capture(), anyInt(), any());
+        verify(mNotificationActivityStarter, times(1))
+                .startNotificationGutsIntent(captor.capture(), anyInt(), any());
         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 906e718..e4d0196 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.Notification;
+import android.app.NotificationChannel;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -35,6 +35,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -43,19 +44,26 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper()
+@RunWithLooper
 @SmallTest
 public class NotificationMenuRowTest extends LeakCheckedTest {
 
+    private ExpandableNotificationRow mRow;
+
     @Before
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+        mRow = mock(ExpandableNotificationRow.class);
+        NotificationData.Entry entry = new NotificationData.Entry(
+                mock(StatusBarNotification.class));
+        entry.channel = mock(NotificationChannel.class);
+        when(mRow.getEntry()).thenReturn(entry);
     }
 
     @Test
     public void testAttachDetach() {
         NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.createMenu(null, null);
+        row.createMenu(mRow, null);
         ViewUtils.attachView(row.getMenuView());
         TestableLooper.get(this).processAllMessages();
         ViewUtils.detachView(row.getMenuView());
@@ -65,9 +73,9 @@
     @Test
     public void testRecreateMenu() {
         NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.createMenu(null, null);
+        row.createMenu(mRow, null);
         assertTrue(row.getMenuView() != null);
-        row.createMenu(null, null);
+        row.createMenu(mRow, null);
         assertTrue(row.getMenuView() != null);
     }
 
@@ -81,12 +89,7 @@
     @Test
     public void testNoAppOpsInSlowSwipe() {
         NotificationMenuRow row = new NotificationMenuRow(mContext);
-        Notification n = mock(Notification.class);
-        StatusBarNotification sbn = mock(StatusBarNotification.class);
-        when(sbn.getNotification()).thenReturn(n);
-        ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
-        when(parent.getStatusBarNotification()).thenReturn(sbn);
-        row.createMenu(parent, null);
+        row.createMenu(mRow, null);
 
         ViewGroup container = (ViewGroup) row.getMenuView();
         // one for snooze and one for noti blocking
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 4b94a25..fed66af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -18,6 +18,7 @@
 
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 import android.widget.RemoteViews;
@@ -34,13 +35,14 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class NotificationCustomViewWrapperTest extends SysuiTestCase {
 
     private ExpandableNotificationRow mRow;
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mRow = new NotificationTestHelper(mContext).createRow();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 2728453..bbafb4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -18,13 +18,14 @@
 
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.NotificationHeaderView;
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -33,16 +34,16 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class NotificationChildrenContainerTest extends SysuiTestCase {
 
     private ExpandableNotificationRow mGroup;
-    private int mId;
     private NotificationTestHelper mNotificationTestHelper;
     private NotificationChildrenContainer mChildrenContainer;
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         mNotificationTestHelper = new NotificationTestHelper(mContext);
         mGroup = mNotificationTestHelper.createGroup();
         mChildrenContainer = mGroup.getChildrenContainer();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 662e016..8ae7d52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -25,6 +25,7 @@
 
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.systemui.SysuiTestCase;
@@ -41,7 +42,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class NotificationRoundnessManagerTest extends SysuiTestCase {
 
     private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
@@ -52,6 +53,7 @@
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
         mFirst = testHelper.createRow();
         mFirst.setHeadsUpAnimatingAwayListener(animatingAway
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index c99e766..4f6329c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -23,18 +23,18 @@
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -43,7 +43,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
 
     private final NotificationStackScrollLayout mStackScroller =
@@ -58,6 +58,7 @@
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
         mFirst = testHelper.createRow();
         mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 7f8668f..f7a95c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -35,7 +36,6 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
-import android.content.res.ColorStateList;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardHostView;
@@ -56,7 +56,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class KeyguardBouncerTest extends SysuiTestCase {
 
     @Mock
@@ -78,6 +78,7 @@
 
     @Before
     public void setup() {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         DejankUtils.setImmediate(true);
         final ViewGroup container = new FrameLayout(getContext());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 31014b8..27ed9c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -30,7 +30,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
 
     private static final int SCREEN_HEIGHT = 2000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java
index 5429153..c587555 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardPresentationTest.java
@@ -20,21 +20,20 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
-import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 public class KeyguardPresentationTest extends SysuiTestCase {
     @Test
     public void testInflation_doesntCrash() {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         LayoutInflater inflater = LayoutInflater.from(getContext());
         inflater.inflate(R.layout.keyguard_presentation, null);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index 8fc15b2..cdaa242 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -29,7 +29,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -38,11 +37,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import com.android.systemui.R;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.SysuiTestCase;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.support.test.filters.SmallTest;
@@ -51,6 +45,11 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.IOverviewProxy;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -59,7 +58,7 @@
 
 /** atest QuickStepControllerTest */
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 @SmallTest
 public class QuickStepControllerTest extends SysuiTestCase {
     private QuickStepController mController;
@@ -78,6 +77,7 @@
         mProxyService = mock(OverviewProxyService.class);
         mProxy = mock(IOverviewProxy.Stub.class);
         doReturn(mProxy).when(mProxyService).getProxy();
+        doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
         mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
 
         mStatusBar = mock(StatusBar.class);
@@ -106,6 +106,18 @@
     }
 
     @Test
+    public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
+        doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
+        mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
+                null /* swipeLeftAction */, null /* swipeRightAction */);
+
+        MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
+        assertFalse(mController.onInterceptTouchEvent(ev));
+        verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
+        assertNull(mController.getCurrentAction());
+    }
+
+    @Test
     public void testHasActionDetectGesturesTouchdown() throws Exception {
         MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 59a4937..27123e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -70,7 +70,7 @@
                 mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
                 statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
-                mock(ActivityLaunchAnimator.Callback.class));
+                mock(ActivityLaunchAnimator.class));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e9e8eb7..c207fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -72,6 +72,8 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -123,6 +125,7 @@
     @Mock private IStatusBarService mBarService;
     @Mock private IDreamManager mDreamManager;
     @Mock private ScrimController mScrimController;
+    @Mock private DozeScrimController mDozeScrimController;
     @Mock private ArrayList<Entry> mNotificationList;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
@@ -211,7 +214,7 @@
                 mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class),
                 mock(NotificationGroupAlertTransferHelper.class), mock(FalsingManager.class),
                 mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
-                mock(DozeScrimController.class), mock(NotificationShelf.class),
+                mDozeScrimController, mock(NotificationShelf.class),
                 mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
                 mock(BubbleController.class));
         mStatusBar.mContext = mContext;
@@ -570,7 +573,28 @@
     }
 
     @Test
+    public void testPulseWhileDozing_updatesScrimController() {
+        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+        mStatusBar.showKeyguardImpl();
 
+        // Keep track of callback to be able to stop the pulse
+        DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+        doAnswer(invocation -> {
+            pulseCallback[0] = invocation.getArgument(0);
+            return null;
+        }).when(mDozeScrimController).pulse(any(), anyInt());
+
+        // Starting a pulse should change the scrim controller to the pulsing state
+        mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
+                DozeLog.PULSE_REASON_NOTIFICATION);
+        verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
+
+        // Ending a pulse should take it back to keyguard state
+        pulseCallback[0].onPulseFinished();
+        verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+    }
+
+    @Test
     public void testSetState_changesIsFullScreenUserSwitcherState() {
         mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
         assertFalse(mStatusBar.isFullScreenUserSwitcherState());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index dcd531d..090963b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -35,7 +36,7 @@
 
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper
 @SmallTest
 public class SystemUIDialogTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
new file mode 100644
index 0000000..93c97ec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CallbackControllerTest extends SysuiTestCase {
+
+    @Test
+    public void testAddCallback() {
+        Lifecycle lifecycle = mock(Lifecycle.class);
+        LifecycleOwner owner = () -> lifecycle;
+        Object callback = new Object();
+        Controller controller = mock(Controller.class);
+
+        // observe and get the lifecycle observer that gets registered.
+        ArgumentCaptor<LifecycleEventObserver> observer =
+                ArgumentCaptor.forClass(LifecycleEventObserver.class);
+        controller.observe(owner, callback);
+        verify(lifecycle).addObserver(observer.capture());
+
+        // move to resume state and make sure the callback gets registered.
+        observer.getValue().onStateChanged(owner, Event.ON_RESUME);
+        verify(controller).addCallback(eq(callback));
+    }
+
+    @Test
+    public void testRemoveCallback() {
+        Lifecycle lifecycle = mock(Lifecycle.class);
+        LifecycleOwner owner = () -> lifecycle;
+        Object callback = new Object();
+        Controller controller = mock(Controller.class);
+
+        // observe and get the lifecycle observer that gets registered.
+        ArgumentCaptor<LifecycleEventObserver> observer =
+                ArgumentCaptor.forClass(LifecycleEventObserver.class);
+        controller.observe(owner, callback);
+        verify(lifecycle).addObserver(observer.capture());
+
+        // move to pause state and make sure the callback gets unregistered.
+        observer.getValue().onStateChanged(owner, Event.ON_PAUSE);
+        verify(controller).removeCallback(eq(callback));
+    }
+
+    private static class Controller implements CallbackController<Object> {
+        @Override
+        public void addCallback(Object listener) {
+        }
+
+        @Override
+        public void removeCallback(Object listener) {
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 3164c04..b3ac6be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -32,9 +32,10 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.util.Assert;
 
 import org.junit.After;
 import org.junit.Before;
@@ -44,7 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 @SmallTest
 public class RemoteInputViewTest extends SysuiTestCase {
 
@@ -60,6 +61,7 @@
 
     @Before
     public void setUp() throws Exception {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
 
         mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
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 df7aeab..506fa97 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
@@ -801,4 +801,55 @@
         assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
         assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
     }
+
+    @Test
+    public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
+        String[] choices = new String[] {"Reply"};
+        String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new String[0], 2,
+                createActions(new String[] {
+                        "Looooooong \nactioooon", "second \naction", "third \naction"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        // smart replies
+        assertReplyButtonHidden(mView.getChildAt(0));
+        // smart actions
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+    }
+
+    @Test
+    public void testMeasure_choicesAndActionsPrioritizeActions() {
+        String[] choices = new String[] {"Short", "longer reply"};
+        String[] actions = new String[] {"Looooooong actioooon", "second action"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
+                createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        Button firstAction = ((Button) mView.getChildAt(1));
+
+        assertEqualMeasures(expectedView, mView);
+        // smart replies
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonHidden(mView.getChildAt(1));
+        // smart actions
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
new file mode 100644
index 0000000..d4e38d8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.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 com.android.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.lifecycle.LifecycleEventObserver;
+
+import com.android.systemui.SysuiBaseFragmentTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LifecycleFragmentTest extends SysuiBaseFragmentTest {
+
+    public LifecycleFragmentTest() {
+        super(LifecycleFragment.class);
+    }
+
+    @Test
+    public void testCreateLifecycle() {
+        LifecycleFragment fragment = (LifecycleFragment) mFragment;
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        fragment.getLifecycle().addObserver(observer);
+
+        mFragments.dispatchCreate();
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(fragment), eq(ON_CREATE));
+    }
+
+    @Test
+    public void testResumeLifecycle() {
+        LifecycleFragment fragment = (LifecycleFragment) mFragment;
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        fragment.getLifecycle().addObserver(observer);
+
+        mFragments.dispatchResume();
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(fragment), eq(ON_RESUME));
+    }
+
+    @Test
+    public void testStartLifecycle() {
+        LifecycleFragment fragment = (LifecycleFragment) mFragment;
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        fragment.getLifecycle().addObserver(observer);
+
+        mFragments.dispatchStart();
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(fragment), eq(ON_START));
+    }
+
+    @Test
+    public void testStopLifecycle() {
+        LifecycleFragment fragment = (LifecycleFragment) mFragment;
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        fragment.getLifecycle().addObserver(observer);
+
+        mFragments.dispatchStart();
+        TestableLooper.get(this).processAllMessages();
+        mFragments.dispatchStop();
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(fragment), eq(ON_STOP));
+    }
+
+    @Test
+    public void testPauseLifecycle() {
+        LifecycleFragment fragment = (LifecycleFragment) mFragment;
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        fragment.getLifecycle().addObserver(observer);
+
+        mFragments.dispatchResume();
+        TestableLooper.get(this).processAllMessages();
+        mFragments.dispatchPause();
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(fragment), eq(ON_PAUSE));
+    }
+
+    @Test
+    public void testDestroyLifecycle() {
+        LifecycleFragment fragment = (LifecycleFragment) mFragment;
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        fragment.getLifecycle().addObserver(observer);
+
+        mFragments.dispatchCreate();
+        TestableLooper.get(this).processAllMessages();
+        mFragments.dispatchDestroy();
+        TestableLooper.get(this).processAllMessages();
+        mFragments = null;
+
+        verify(observer).onStateChanged(eq(fragment), eq(ON_DESTROY));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
new file mode 100644
index 0000000..d63bbe6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.systemui.util;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
+import android.view.View;
+
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SysuiLifecycleTest extends SysuiTestCase {
+
+    @Test
+    public void testAttach() {
+        View v = new View(mContext);
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        LifecycleOwner lifecycle = viewAttachLifecycle(v);
+        lifecycle.getLifecycle().addObserver(observer);
+
+        ViewUtils.attachView(v);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(lifecycle), eq(ON_CREATE));
+        verify(observer).onStateChanged(eq(lifecycle), eq(ON_START));
+        verify(observer).onStateChanged(eq(lifecycle), eq(ON_RESUME));
+
+        ViewUtils.detachView(v);
+        TestableLooper.get(this).processAllMessages();
+    }
+
+    @Test
+    public void testDetach() {
+        View v = new View(mContext);
+        LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
+        LifecycleOwner lifecycle = viewAttachLifecycle(v);
+        lifecycle.getLifecycle().addObserver(observer);
+
+        ViewUtils.attachView(v);
+        TestableLooper.get(this).processAllMessages();
+
+        ViewUtils.detachView(v);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(observer).onStateChanged(eq(lifecycle), eq(ON_PAUSE));
+        verify(observer).onStateChanged(eq(lifecycle), eq(ON_STOP));
+        verify(observer).onStateChanged(eq(lifecycle), eq(ON_DESTROY));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
index 43942f7..ab9b0c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -20,11 +20,13 @@
 import static org.mockito.Mockito.verify;
 
 import android.animation.Animator;
+import android.os.Looper;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -33,7 +35,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 public class KeepAwakeAnimationListenerTest extends SysuiTestCase {
     @Mock WakeLock mWakeLock;
@@ -41,6 +43,7 @@
 
     @Before
     public void setup() {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         KeepAwakeAnimationListener.sWakeLock = mWakeLock;
         mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext());
@@ -55,4 +58,10 @@
         mKeepAwakeAnimationListener.onAnimationEnd((Animator) null);
         verify(mWakeLock).release();
     }
+
+    @Test(expected = IllegalStateException.class)
+    public void initThrows_onNonMainThread() {
+        Assert.sMainLooper = Looper.getMainLooper();
+        new KeepAwakeAnimationListener(getContext());
+    }
 }
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
new file mode 100644
index 0000000..d316fbd
--- /dev/null
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := AccentColorBlack
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AccentColorBlackOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..3b99648
--- /dev/null
+++ b/packages/overlays/AccentColorBlackOverlay/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+/**
+ * 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.theme.color.black"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/>
+
+    <application android:label="@string/accent_color_black_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
new file mode 100644
index 0000000..5648f91
--- /dev/null
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/colors_device_defaults.xml
@@ -0,0 +1,22 @@
+<?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>
+    <color name="accent_device_default_light">#202020</color>
+    <color name="accent_device_default_dark">#FFFFFF</color>
+</resources>
diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
new file mode 100644
index 0000000..baf09b1
--- /dev/null
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Black accent color name application label. [CHAR LIMIT=50] -->
+    <string name="accent_color_black_overlay" translatable="false">Black Accent Color</string>
+</resources>
+
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
new file mode 100644
index 0000000..afc4287
--- /dev/null
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := AccentColorGreen
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AccentColorGreenOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..609d5be
--- /dev/null
+++ b/packages/overlays/AccentColorGreenOverlay/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+/**
+ * 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.theme.color.green"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/>
+
+    <application android:label="@string/accent_color_green_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
new file mode 100644
index 0000000..089f08c
--- /dev/null
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/colors_device_defaults.xml
@@ -0,0 +1,22 @@
+<?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>
+    <color name="accent_device_default_light">#1B873B</color>
+    <color name="accent_device_default_dark">#84C188</color>
+</resources>
diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
new file mode 100644
index 0000000..4de344c
--- /dev/null
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Green accent color name application label. [CHAR LIMIT=50] -->
+    <string name="accent_color_green_overlay" translatable="false">Green Accent Color</string>
+</resources>
+
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
new file mode 100644
index 0000000..3366169
--- /dev/null
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := AccentColorPurple
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := AccentColorPurpleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..497a358
--- /dev/null
+++ b/packages/overlays/AccentColorPurpleOverlay/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<!--
+/**
+ * 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.theme.color.purple"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <overlay android:targetPackage="android" android:category="android.theme.customization.accent_color" android:priority="1"/>
+
+    <application android:label="@string/accent_color_purple_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
new file mode 100644
index 0000000..7e34bac
--- /dev/null
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/colors_device_defaults.xml
@@ -0,0 +1,22 @@
+<?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>
+    <color name="accent_device_default_light">#725AFF</color>
+    <color name="accent_device_default_dark">#B5A9FC</color>
+</resources>
diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
new file mode 100644
index 0000000..d1eb95a
--- /dev/null
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Purple accent color name application label. [CHAR LIMIT=50] -->
+    <string name="accent_color_purple_overlay" translatable="false">Purple Accent Color</string>
+</resources>
+
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
new file mode 100644
index 0000000..a734a6b
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeRoundedRect
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeRoundedRectOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml b/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..39c082b
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * 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.theme.icon.roundedrect"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.adaptive_icon_shape"
+        android:priority="1"/>
+
+    <application android:label="@string/icon_shape_roundedrect_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
new file mode 100644
index 0000000..f024615
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
+    <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+    <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
new file mode 100644
index 0000000..dc5c196
--- /dev/null
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Rounded corner rectangle overlay -->
+    <string name="icon_shape_roundedrect_overlay" translatable="false">RoundedRectangle Icons</string>
+
+</resources>
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
new file mode 100644
index 0000000..217da9f
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeSquare
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeSquareOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml b/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..235fdeb
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * 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.theme.icon.square"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.adaptive_icon_shape"
+        android:priority="1"/>
+
+    <application android:label="@string/icon_shape_square_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/config.xml b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
new file mode 100644
index 0000000..54623f5
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string>
+    <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+    <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
new file mode 100644
index 0000000..4fd39ff26
--- /dev/null
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Square icon overlay -->
+    <string name="icon_shape_square_overlay" translatable="false">Square Icons</string>
+
+</resources>
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
new file mode 100644
index 0000000..fd3bfa0
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeSquircle
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeSquircleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml b/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..ca618e4
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * 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.theme.icon.squircle"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.adaptive_icon_shape"
+        android:priority="1"/>
+
+    <application android:label="@string/icon_shape_squircle_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeSquircleOverlay/res/values/config.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/config.xml
new file mode 100644
index 0000000..eaf7de3
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z"</string>
+    <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+    <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
new file mode 100644
index 0000000..b7c001c
--- /dev/null
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Squircle icon shape overlay -->
+    <string name="icon_shape_squircle_overlay" translatable="false">Square Icons</string>
+
+</resources>
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
new file mode 100644
index 0000000..ea43423
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -0,0 +1,30 @@
+#
+#  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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeTeardrop
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeTeardropOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml b/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..b7d5ecb
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+/**
+ * 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.theme.icon.teardrop"
+    android:versionCode="1"
+    android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="android.theme.customization.adaptive_icon_shape"
+        android:priority="1"/>
+
+    <application android:label="@string/icon_shape_teardrop_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
new file mode 100644
index 0000000..43ad04d
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z"</string>
+    <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+    <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
new file mode 100644
index 0000000..d946ee8
--- /dev/null
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Teardrop icon overlay -->
+    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop Icons</string>
+
+</resources>
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c56f31e..944ee33 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -70,10 +70,10 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
-import com.android.server.AbstractMasterSystemService;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.intelligence.IntelligenceManagerInternal;
 
 import java.io.FileDescriptor;
@@ -175,10 +175,6 @@
         }
     };
 
-    // TODO(b/117779333): move to superclass / create super-class for ShellCommand
-    @GuardedBy("mLock")
-    private boolean mAllowInstantService;
-
     /**
      * Supported modes for Augmented Autofill Smart Suggestions.
      */
@@ -271,6 +267,11 @@
         addCompatibilityModeRequestsLocked(service, userId);
     }
 
+    @Override // from AbstractMasterSystemService
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+    }
+
     @Override // from SystemService
     public void onStart() {
         publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
@@ -290,7 +291,7 @@
     // Called by Shell command.
     void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             if (userId != UserHandle.USER_ALL) {
@@ -313,7 +314,7 @@
     // Called by Shell command.
     void listSessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "listSessions() for userId " + userId);
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         final Bundle resultData = new Bundle();
         final ArrayList<String> sessions = new ArrayList<>();
@@ -340,7 +341,7 @@
     // Called by Shell command.
     void reset() {
         Slog.i(TAG, "reset()");
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             visitServicesLocked((s) -> s.destroyLocked());
@@ -351,7 +352,7 @@
     // Called by Shell command.
     void setLogLevel(int level) {
         Slog.i(TAG, "setLogLevel(): " + level);
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -388,7 +389,7 @@
 
     // Called by Shell command.
     int getLogLevel() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -399,7 +400,7 @@
 
     // Called by Shell command.
     int getMaxPartitions() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             return sPartitionMaxCount;
@@ -408,8 +409,8 @@
 
     // Called by Shell command.
     void setMaxPartitions(int max) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
+        enforceCallingPermissionForManagement();
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -433,7 +434,7 @@
 
     // Called by Shell command.
     int getMaxVisibleDatasets() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (sLock) {
             return sVisibleDatasetsMaxCount;
@@ -442,8 +443,8 @@
 
     // Called by Shell command.
     void setMaxVisibleDatasets(int max) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
+        enforceCallingPermissionForManagement();
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -480,7 +481,7 @@
     // Called by Shell command.
     void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         final FieldClassificationStrategy strategy =
                 new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
@@ -491,33 +492,16 @@
 
     // Called by Shell command.
     Boolean getFullScreenMode() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
         return sFullScreenMode;
     }
 
     // Called by Shell command.
     void setFullScreenMode(@Nullable Boolean mode) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
         sFullScreenMode = mode;
     }
 
-    // Called by Shell command.
-    boolean getAllowInstantService() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        synchronized (mLock) {
-            return mAllowInstantService;
-        }
-    }
-
-    // Called by Shell command.
-    void setAllowInstantService(boolean mode) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        Slog.i(TAG, "setAllowInstantService(): " + mode);
-        synchronized (mLock) {
-            mAllowInstantService = mode;
-        }
-    }
-
     private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
@@ -1218,7 +1202,6 @@
                     mAutofillCompatState.dump(prefix, pw);
                     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));
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0df99d4..1f229cd 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -70,11 +70,11 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-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 com.android.server.infra.AbstractPerUserSystemService;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -190,6 +190,11 @@
         return mInfo.getServiceInfo();
     }
 
+    @Override // from PerUserSystemService
+    protected String getDefaultComponentName() {
+        return getComponentNameFromSettings();
+    }
+
     @Nullable
     String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
         return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
@@ -369,7 +374,7 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            final String autoFillService = getComponentNameFromSettings();
+            final String autoFillService = getComponentNameLocked();
             final ComponentName componentName = serviceInfo.getComponentName();
             if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
                 mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index af65759..4b7d290 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -40,7 +40,7 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
-import com.android.server.AbstractSinglePendingRequestRemoteService;
+import com.android.server.infra.AbstractSinglePendingRequestRemoteService;
 
 final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4c64507..fb64cb2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -95,10 +95,10 @@
 import com.android.internal.logging.MetricsLogger;
 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.infra.AbstractRemoteService;
 import com.android.server.intelligence.IntelligenceManagerInternal;
 import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
@@ -2579,11 +2579,13 @@
                     + " when server returned null for session " + this.id);
         }
 
+        final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue();
+
         // 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);
+                mActivityToken, this.id, mCurrentViewId, currentValue);
     }
 
     @GuardedBy("mLock")
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
new file mode 100644
index 0000000..f356b4f
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
@@ -0,0 +1,117 @@
+/*
+ * 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.backup.encryption.keys;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Wraps a {@link RecoveryController}'s {@link SecretKey}. These are kept in "AndroidKeyStore" (a
+ * provider for {@link java.security.KeyStore} and {@link javax.crypto.KeyGenerator}. They are also
+ * synced with the recoverable key store, wrapped by the primary key. This allows them to be
+ * recovered on a user's subsequent device through providing their lock screen secret.
+ */
+public class RecoverableKeyStoreSecondaryKey {
+    private static final String TAG = "RecoverableKeyStoreSecondaryKey";
+
+    private final String mAlias;
+    private final SecretKey mSecretKey;
+
+    /**
+     * A new instance.
+     *
+     * @param alias The alias. It is keyed with this in AndroidKeyStore and the recoverable key
+     *     store.
+     * @param secretKey The key.
+     */
+    public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
+        mAlias = checkNotNull(alias);
+        mSecretKey = checkNotNull(secretKey);
+    }
+
+    /**
+     * The ID, as stored in the recoverable {@link java.security.KeyStore}, and as used to identify
+     * wrapped tertiary keys on the backup server.
+     */
+    public String getAlias() {
+        return mAlias;
+    }
+
+    /** The secret key, to be used to wrap tertiary keys. */
+    public SecretKey getSecretKey() {
+        return mSecretKey;
+    }
+
+    /**
+     * The status of the key. i.e., whether it's been synced to remote trusted hardware.
+     *
+     * @param context The application context.
+     * @return One of {@link Status#SYNCED}, {@link Status#NOT_SYNCED} or {@link Status#DESTROYED}.
+     */
+    public @Status int getStatus(Context context) {
+        try {
+            return getStatusInternal(context);
+        } catch (InternalRecoveryServiceException e) {
+            Slog.wtf(TAG, "Internal error getting recovery status", e);
+            // Return NOT_SYNCED by default, as we do not want the backups to fail or to repeatedly
+            // attempt to reinitialize.
+            return Status.NOT_SYNCED;
+        }
+    }
+
+    private @Status int getStatusInternal(Context context) throws InternalRecoveryServiceException {
+        int status = RecoveryController.getInstance(context).getRecoveryStatus(mAlias);
+        switch (status) {
+            case RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE:
+                return Status.DESTROYED;
+            case RecoveryController.RECOVERY_STATUS_SYNCED:
+                return Status.SYNCED;
+            case RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS:
+                return Status.NOT_SYNCED;
+            default:
+                // Throw an exception if we encounter a status that doesn't match any of the above.
+                throw new InternalRecoveryServiceException(
+                        "Unexpected status from getRecoveryStatus: " + status);
+        }
+    }
+
+    /** Status of a key in the recoverable key store. */
+    @IntDef({Status.NOT_SYNCED, Status.SYNCED, Status.DESTROYED})
+    public @interface Status {
+        /**
+         * The key has not yet been synced to remote trusted hardware. This may be because the user
+         * has not yet unlocked their device.
+         */
+        int NOT_SYNCED = 1;
+
+        /**
+         * The key has been synced with remote trusted hardware. It should now be recoverable on
+         * another device.
+         */
+        int SYNCED = 2;
+
+        /** The key has been lost forever. This can occur if the user disables their lock screen. */
+        int DESTROYED = 3;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
new file mode 100644
index 0000000..db5fe77
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
@@ -0,0 +1,119 @@
+/*
+ * 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.backup.encryption.keys;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.ByteStringUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Manages generating, deleting, and retrieving secondary keys through {@link RecoveryController}.
+ *
+ * <p>The recoverable key store will be synced remotely via the {@link RecoveryController}, allowing
+ * recovery of keys on other devices owned by the user.
+ */
+public class RecoverableKeyStoreSecondaryKeyManager {
+    private static final String BACKUP_KEY_ALIAS_PREFIX =
+            "com.android.server.backup/recoverablekeystore/";
+    private static final int BACKUP_KEY_SUFFIX_LENGTH_BITS = 128;
+    private static final int BITS_PER_BYTE = 8;
+
+    /** A new instance. */
+    public static RecoverableKeyStoreSecondaryKeyManager getInstance(Context context) {
+        return new RecoverableKeyStoreSecondaryKeyManager(
+                RecoveryController.getInstance(context), new SecureRandom());
+    }
+
+    private final RecoveryController mRecoveryController;
+    private final SecureRandom mSecureRandom;
+
+    @VisibleForTesting
+    public RecoverableKeyStoreSecondaryKeyManager(
+            RecoveryController recoveryController, SecureRandom secureRandom) {
+        mRecoveryController = recoveryController;
+        mSecureRandom = secureRandom;
+    }
+
+    /**
+     * Generates a new recoverable key using the {@link RecoveryController}.
+     *
+     * @throws InternalRecoveryServiceException if an unexpected error occurred generating the key.
+     * @throws LockScreenRequiredException if the user does not have a lock screen. A lock screen is
+     *     required to generate a recoverable key.
+     */
+    public RecoverableKeyStoreSecondaryKey generate()
+            throws InternalRecoveryServiceException, LockScreenRequiredException,
+                    UnrecoverableKeyException {
+        String alias = generateId();
+        mRecoveryController.generateKey(alias);
+        SecretKey key = (SecretKey) mRecoveryController.getKey(alias);
+        if (key == null) {
+            throw new InternalRecoveryServiceException(
+                    String.format(
+                            "Generated key %s but could not get it back immediately afterwards.",
+                            alias));
+        }
+        return new RecoverableKeyStoreSecondaryKey(alias, key);
+    }
+
+    /**
+     * Removes the secondary key. This means the key will no longer be recoverable.
+     *
+     * @param alias The alias of the key.
+     * @throws InternalRecoveryServiceException if there was a {@link RecoveryController} error.
+     */
+    public void remove(String alias) throws InternalRecoveryServiceException {
+        mRecoveryController.removeKey(alias);
+    }
+
+    /**
+     * Returns the {@link RecoverableKeyStoreSecondaryKey} with {@code alias} if it is in the {@link
+     * RecoveryController}. Otherwise, {@link Optional#empty()}.
+     */
+    public Optional<RecoverableKeyStoreSecondaryKey> get(String alias)
+            throws InternalRecoveryServiceException, UnrecoverableKeyException {
+        SecretKey secretKey = (SecretKey) mRecoveryController.getKey(alias);
+        return Optional.ofNullable(secretKey)
+                .map(key -> new RecoverableKeyStoreSecondaryKey(alias, key));
+    }
+
+    /**
+     * Generates a new key alias. This has more entropy than a UUID - it can be considered
+     * universally unique.
+     */
+    private String generateId() {
+        byte[] id = new byte[BACKUP_KEY_SUFFIX_LENGTH_BITS / BITS_PER_BYTE];
+        mSecureRandom.nextBytes(id);
+        return BACKUP_KEY_ALIAS_PREFIX + ByteStringUtils.toHexString(id);
+    }
+
+    /** Constructs a {@link RecoverableKeyStoreSecondaryKeyManager}. */
+    public interface RecoverableKeyStoreSecondaryKeyManagerProvider {
+        /** Returns a newly constructed {@link RecoverableKeyStoreSecondaryKeyManager}. */
+        RecoverableKeyStoreSecondaryKeyManager get();
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
new file mode 100644
index 0000000..ebf09df
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * 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.backup.encryption.keys;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/** 256-bit AES key generator. Each app should have its own separate AES key. */
+public class TertiaryKeyGenerator {
+    private static final int KEY_SIZE_BITS = 256;
+    private static final String KEY_ALGORITHM = "AES";
+
+    private final KeyGenerator mKeyGenerator;
+
+    /** New instance generating keys using {@code secureRandom}. */
+    public TertiaryKeyGenerator(SecureRandom secureRandom) {
+        try {
+            mKeyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+            mKeyGenerator.init(KEY_SIZE_BITS, secureRandom);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(
+                    "Impossible condition: JCE thinks it does not support AES.", e);
+        }
+    }
+
+    /** Generates a new random AES key. */
+    public SecretKey generate() {
+        return mKeyGenerator.generateKey();
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
new file mode 100644
index 0000000..ec90f6c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
@@ -0,0 +1,113 @@
+/*
+ * 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.backup.encryption.keys;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+
+/**
+ * Tracks when a tertiary key rotation is due.
+ *
+ * <p>After a certain number of incremental backups, the device schedules a full backup, which will
+ * generate a new encryption key, effecting a key rotation. We should do this on a regular basis so
+ * that if a key does become compromised it has limited value to the attacker.
+ *
+ * <p>No additional synchronization of this class is provided. Only one instance should be used at
+ * any time. This should be fine as there should be no parallelism in backups.
+ */
+public class TertiaryKeyRotationTracker {
+    private static final int MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION = 31;
+    private static final String SHARED_PREFERENCES_NAME = "tertiary_key_rotation_tracker";
+
+    private static final String TAG = "TertiaryKeyRotationTracker";
+    private static final boolean DEBUG = false;
+
+    /**
+     * A new instance, using {@code context} to commit data to disk via {@link SharedPreferences}.
+     */
+    public static TertiaryKeyRotationTracker getInstance(Context context) {
+        return new TertiaryKeyRotationTracker(
+                context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE));
+    }
+
+    private final SharedPreferences mSharedPreferences;
+
+    /** New instance, storing data in {@code mSharedPreferences}. */
+    @VisibleForTesting
+    TertiaryKeyRotationTracker(SharedPreferences sharedPreferences) {
+        mSharedPreferences = sharedPreferences;
+    }
+
+    /**
+     * Returns {@code true} if the given app is due having its key rotated.
+     *
+     * @param packageName The package name of the app.
+     */
+    public boolean isKeyRotationDue(String packageName) {
+        return getBackupsSinceRotation(packageName) >= MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION;
+    }
+
+    /**
+     * Records that an incremental backup has occurred. Each incremental backup brings the app
+     * closer to the time when its key should be rotated.
+     *
+     * @param packageName The package name of the app for which the backup occurred.
+     */
+    public void recordBackup(String packageName) {
+        int backupsSinceRotation = getBackupsSinceRotation(packageName) + 1;
+        mSharedPreferences.edit().putInt(packageName, backupsSinceRotation).apply();
+        if (DEBUG) {
+            Slog.d(
+                    TAG,
+                    String.format(
+                            Locale.US,
+                            "Incremental backup for %s. %d backups until key rotation.",
+                            packageName,
+                            Math.max(
+                                    0,
+                                    MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION
+                                            - backupsSinceRotation)));
+        }
+    }
+
+    /**
+     * Resets the rotation delay for the given app. Should be invoked after a key rotation.
+     *
+     * @param packageName Package name of the app whose key has rotated.
+     */
+    public void resetCountdown(String packageName) {
+        mSharedPreferences.edit().putInt(packageName, 0).apply();
+    }
+
+    /** Marks all enrolled packages for key rotation. */
+    public void markAllForRotation() {
+        SharedPreferences.Editor editor = mSharedPreferences.edit();
+        for (String packageName : mSharedPreferences.getAll().keySet()) {
+            editor.putInt(packageName, MAX_BACKUPS_UNTIL_TERTIARY_KEY_ROTATION);
+        }
+        editor.apply();
+    }
+
+    private int getBackupsSinceRotation(String packageName) {
+        return mSharedPreferences.getInt(packageName, 0);
+    }
+}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 01716a0..11a2fc9 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -16,23 +16,33 @@
 
 package com.android.server;
 
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
 import android.app.AppGlobals;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.ThreadLocalWorkSource;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.CallSession;
 import com.android.internal.os.CachedDeviceState;
 
 import java.io.FileDescriptor;
@@ -49,6 +59,106 @@
     private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
             = "persist.sys.binder_calls_detailed_tracking";
 
+    /** Resolves the work source of an incoming binder transaction. */
+    static class WorkSourceProvider {
+        private ArraySet<Integer> mAppIdWhitelist;
+
+        WorkSourceProvider() {
+            mAppIdWhitelist = new ArraySet<>();
+        }
+
+        public int resolveWorkSourceUid() {
+            final int callingUid = getCallingUid();
+            final int appId = UserHandle.getAppId(callingUid);
+            if (mAppIdWhitelist.contains(appId)) {
+                final int workSource = getCallingWorkSourceUid();
+                final boolean isWorkSourceSet = workSource != Binder.UNSET_WORKSOURCE;
+                return isWorkSourceSet ?  workSource : callingUid;
+            }
+            return callingUid;
+        }
+
+        public void systemReady(Context context) {
+            mAppIdWhitelist = createAppidWhitelist(context);
+        }
+
+        public void dump(PrintWriter pw, Map<Integer, String> appIdToPackageName) {
+            pw.println("AppIds of apps that can set the work source:");
+            final ArraySet<Integer> whitelist = mAppIdWhitelist;
+            for (Integer appId : whitelist) {
+                pw.println("\t- " + appIdToPackageName.getOrDefault(appId, String.valueOf(appId)));
+            }
+        }
+
+        protected int getCallingUid() {
+            return Binder.getCallingUid();
+        }
+
+        protected int getCallingWorkSourceUid() {
+            return Binder.getCallingWorkSourceUid();
+        }
+
+        private ArraySet<Integer> createAppidWhitelist(Context context) {
+            // Use a local copy instead of mAppIdWhitelist to prevent concurrent read access.
+            final ArraySet<Integer> whitelist = new ArraySet<>();
+
+            // We trust our own process.
+            whitelist.add(Process.myUid());
+            // We only need to initialize it once. UPDATE_DEVICE_STATS is a system permission.
+            final PackageManager pm = context.getPackageManager();
+            final String[] permissions = { android.Manifest.permission.UPDATE_DEVICE_STATS };
+            final int queryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            final List<PackageInfo> packages =
+                    pm.getPackagesHoldingPermissions(permissions, queryFlags);
+            final int packagesSize = packages.size();
+            for (int i = 0; i < packagesSize; i++) {
+                final PackageInfo pkgInfo = packages.get(i);
+                try {
+                    final int uid = pm.getPackageUid(pkgInfo.packageName, queryFlags);
+                    final int appId = UserHandle.getAppId(uid);
+                    whitelist.add(appId);
+                } catch (NameNotFoundException e) {
+                    Slog.e(TAG, "Cannot find uid for package name " + pkgInfo.packageName, e);
+                }
+            }
+            return whitelist;
+        }
+    }
+
+    /** Observer for all system server incoming binder transactions. */
+    @VisibleForTesting
+    static class BinderCallsObserver implements BinderInternal.Observer {
+        private final BinderInternal.Observer mBinderCallsStats;
+        private final WorkSourceProvider mWorkSourceProvider;
+
+        BinderCallsObserver(BinderInternal.Observer callsStats,
+                WorkSourceProvider workSourceProvider) {
+            mBinderCallsStats = callsStats;
+            mWorkSourceProvider = workSourceProvider;
+        }
+
+        @Override
+        public CallSession callStarted(Binder binder, int code) {
+            // We depend on the code in Binder#execTransact to reset the state of
+            // ThreadLocalWorkSource
+            setThreadLocalWorkSourceUid(mWorkSourceProvider.resolveWorkSourceUid());
+            return mBinderCallsStats.callStarted(binder, code);
+        }
+
+        @Override
+        public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+            mBinderCallsStats.callEnded(s, parcelRequestSize, parcelReplySize);
+        }
+
+        @Override
+        public void callThrewException(CallSession s, Exception exception) {
+            mBinderCallsStats.callThrewException(s, exception);
+        }
+
+        protected void setThreadLocalWorkSourceUid(int uid) {
+            ThreadLocalWorkSource.setUid(uid);
+        }
+    }
 
     /** Listens for flag changes. */
     private static class SettingsObserver extends ContentObserver {
@@ -63,13 +173,16 @@
         private final Context mContext;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
         private final BinderCallsStats mBinderCallsStats;
+        private final BinderCallsObserver mBinderCallsObserver;
 
-        public SettingsObserver(Context context, BinderCallsStats binderCallsStats) {
+        SettingsObserver(Context context, BinderCallsStats binderCallsStats,
+                BinderCallsObserver observer) {
             super(BackgroundThread.getHandler());
             mContext = context;
             context.getContentResolver().registerContentObserver(mUri, false, this,
                     UserHandle.USER_SYSTEM);
             mBinderCallsStats = binderCallsStats;
+            mBinderCallsObserver = observer;
             // Always kick once to ensure that we match current state
             onChange();
         }
@@ -107,7 +220,7 @@
                     mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
             if (mEnabled != enabled) {
                 if (enabled) {
-                    Binder.setObserver(mBinderCallsStats);
+                    Binder.setObserver(mBinderCallsObserver);
                     Binder.setProxyTransactListener(
                             new Binder.PropagateWorkSourceTransactListener());
                 } else {
@@ -116,6 +229,7 @@
                 }
                 mEnabled = enabled;
                 mBinderCallsStats.reset();
+                mBinderCallsStats.setAddDebugEntries(enabled);
             }
         }
     }
@@ -154,6 +268,7 @@
     public static class LifeCycle extends SystemService {
         private BinderCallsStatsService mService;
         private BinderCallsStats mBinderCallsStats;
+        private WorkSourceProvider mWorkSourceProvider;
 
         public LifeCycle(Context context) {
             super(context);
@@ -162,7 +277,11 @@
         @Override
         public void onStart() {
             mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
-            mService = new BinderCallsStatsService(mBinderCallsStats);
+            mWorkSourceProvider = new WorkSourceProvider();
+            BinderCallsObserver binderCallsObserver =
+                    new BinderCallsObserver(mBinderCallsStats, mWorkSourceProvider);
+            mService = new BinderCallsStatsService(
+                    mBinderCallsStats, binderCallsObserver, mWorkSourceProvider);
             publishLocalService(Internal.class, new Internal(mBinderCallsStats));
             publishBinderService("binder_calls_stats", mService);
             boolean detailedTrackingEnabled = SystemProperties.getBoolean(
@@ -181,21 +300,29 @@
             if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
                 CachedDeviceState.Readonly deviceState = getLocalService(
                         CachedDeviceState.Readonly.class);
-                mService.systemReady(getContext());
                 mBinderCallsStats.setDeviceState(deviceState);
+                // It needs to be called before mService.systemReady to make sure the observer is
+                // initialized before installing it.
+                mWorkSourceProvider.systemReady(getContext());
+                mService.systemReady(getContext());
             }
         }
     }
 
     private SettingsObserver mSettingsObserver;
     private final BinderCallsStats mBinderCallsStats;
+    private final BinderCallsObserver mBinderCallsObserver;
+    private final WorkSourceProvider mWorkSourceProvider;
 
-    BinderCallsStatsService(BinderCallsStats binderCallsStats) {
+    BinderCallsStatsService(BinderCallsStats binderCallsStats, BinderCallsObserver observer,
+            WorkSourceProvider workSourceProvider) {
         mBinderCallsStats = binderCallsStats;
+        mBinderCallsObserver = observer;
+        mWorkSourceProvider = workSourceProvider;
     }
 
     public void systemReady(Context context) {
-        mSettingsObserver = new SettingsObserver(context, mBinderCallsStats);
+        mSettingsObserver = new SettingsObserver(context, mBinderCallsStats, mBinderCallsObserver);
     }
 
     public void reset() {
@@ -215,7 +342,7 @@
                     pw.println("binder_calls_stats reset.");
                     return;
                 } else if ("--enable".equals(arg)) {
-                    Binder.setObserver(mBinderCallsStats);
+                    Binder.setObserver(mBinderCallsObserver);
                     return;
                 } else if ("--disable".equals(arg)) {
                     Binder.setObserver(null);
@@ -233,6 +360,9 @@
                     mBinderCallsStats.setDetailedTracking(false);
                     pw.println("Detailed tracking disabled");
                     return;
+                } else if ("--dump-worksource-provider".equals(arg)) {
+                    mWorkSourceProvider.dump(pw, getAppIdToPackagesMap());
+                    return;
                 } else if ("-h".equals(arg)) {
                     pw.println("binder_calls_stats commands:");
                     pw.println("  --reset: Reset stats");
@@ -271,5 +401,4 @@
         }
         return map;
     }
-
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bb3b9f7..14503f9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -274,7 +274,8 @@
     private int mDefaultInetConditionPublished = 0;
 
     private INetworkManagementService mNMS;
-    private INetd mNetd;
+    @VisibleForTesting
+    protected INetd mNetd;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -2090,7 +2091,8 @@
         }
     }
 
-    private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
+    @VisibleForTesting
+    protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
     private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
 
     // Overridden for testing purposes to avoid writing to SystemProperties.
@@ -2116,15 +2118,11 @@
         try {
             if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
 
-            final String prefix = "/sys/kernel/ipv4/tcp_";
-            FileUtils.stringToFile(prefix + "rmem_min", values[0]);
-            FileUtils.stringToFile(prefix + "rmem_def", values[1]);
-            FileUtils.stringToFile(prefix + "rmem_max", values[2]);
-            FileUtils.stringToFile(prefix + "wmem_min", values[3]);
-            FileUtils.stringToFile(prefix + "wmem_def", values[4]);
-            FileUtils.stringToFile(prefix + "wmem_max", values[5]);
+            String rmemValues = String.join(" ", values[0], values[1], values[2]);
+            String wmemValues = String.join(" ", values[3], values[4], values[5]);
+            mNetd.setTcpRWmemorySize(rmemValues, wmemValues);
             mCurrentTcpBufferSizes = tcpBufferSizes;
-        } catch (IOException e) {
+        } catch (RemoteException | ServiceSpecificException e) {
             loge("Can't set TCP buffer sizes:" + e);
         }
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 7ee3d3b..126bf65 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1236,7 +1236,8 @@
                     OsConstants.UDP_ENCAP,
                     OsConstants.UDP_ENCAP_ESPINUDP);
 
-            mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
+            mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
+                        new ParcelFileDescriptor(sockFd), callingUid);
             if (port != 0) {
                 Log.v(TAG, "Binding to port " + port);
                 Os.bind(sockFd, INADDR_ANY, port);
@@ -1696,7 +1697,7 @@
         mSrvConfig
                 .getNetdInstance()
                 .ipSecApplyTransportModeTransform(
-                        socket.getFileDescriptor(),
+                        socket,
                         callingUid,
                         direction,
                         c.getSourceAddress(),
@@ -1715,7 +1716,7 @@
             throws RemoteException {
         mSrvConfig
                 .getNetdInstance()
-                .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
+                .ipSecRemoveTransportModeTransform(socket);
     }
 
     /**
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index c563ad2..fa3baba 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -94,6 +94,8 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         pw.print("Start time: ");
         pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis()));
+        pw.print("On battery time (ms): ");
+        pw.println(mStats.getBatteryTimeMillis());
         final List<LooperStats.ExportedEntry> entries = mStats.getEntries();
         entries.sort(Comparator
                 .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java
index ccfac80..bb39ccc 100644
--- a/services/core/java/com/android/server/RuntimeService.java
+++ b/services/core/java/com/android/server/RuntimeService.java
@@ -94,7 +94,7 @@
         // Add /data tz data set using the DistroVersion class (which libcore cannot use).
         // This update mechanism will be removed after the time zone APEX is launched so this
         // untidiness will disappear with it.
-        String debugKeyPrefix = "core_library.timezone.data_";
+        String debugKeyPrefix = "core_library.timezone.source.data_";
         String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
                 TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
         addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e933bd0..dbea529 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
 import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT;
@@ -99,6 +101,7 @@
 import android.os.storage.VolumeRecord;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.sysprop.VoldProperties;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -1015,7 +1018,7 @@
 
         // On an encrypted device we can't see system properties yet, so pull
         // the system locale out of the mount service.
-        if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+        if ("".equals(VoldProperties.encrypt_progress().orElse(""))) {
             copyLocaleFromMountService();
         }
     }
@@ -2757,6 +2760,14 @@
     public @Nullable ParcelFileDescriptor openProxyFileDescriptor(
             int mountId, int fileId, int mode) {
         Slog.v(TAG, "mountProxyFileDescriptor");
+
+        // We only support a narrow set of incoming mode flags
+        if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
+            mode = MODE_READ_WRITE;
+        } else {
+            mode = MODE_READ_ONLY;
+        }
+
         try {
             synchronized (mAppFuseLock) {
                 if (mAppFuseBridge == null) {
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index 8cd9d188..15de3de 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -133,7 +133,7 @@
             proto.write(ActiveInstrumentationProto.TARGET_PROCESSES, p);
         }
         if (mTargetInfo != null) {
-            mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO);
+            mTargetInfo.writeToProto(proto, ActiveInstrumentationProto.TARGET_INFO, 0);
         }
         proto.write(ActiveInstrumentationProto.PROFILE_FILE, mProfileFile);
         proto.write(ActiveInstrumentationProto.WATCHER, mWatcher.toString());
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a19e928..23287cf 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1801,16 +1801,25 @@
         for (int i = clist.size() - 1; i >= 0; i--) {
             final ConnectionRecord crec = clist.get(i);
             final ServiceRecord srec = crec.binding.service;
-            if (srec != null && srec.app != null
-                    && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
-                if (group > 0) {
-                    srec.app.connectionService = srec;
-                    srec.app.connectionGroup = group;
-                    srec.app.connectionImportance = importance;
+            if (srec != null && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+                if (srec.app != null) {
+                    if (group > 0) {
+                        srec.app.connectionService = srec;
+                        srec.app.connectionGroup = group;
+                        srec.app.connectionImportance = importance;
+                    } else {
+                        srec.app.connectionService = null;
+                        srec.app.connectionGroup = 0;
+                        srec.app.connectionImportance = 0;
+                    }
                 } else {
-                    srec.app.connectionService = null;
-                    srec.app.connectionGroup = 0;
-                    srec.app.connectionImportance = 0;
+                    if (group > 0) {
+                        srec.pendingConnectionGroup = group;
+                        srec.pendingConnectionImportance = importance;
+                    } else {
+                        srec.pendingConnectionGroup = 0;
+                        srec.pendingConnectionImportance = 0;
+                    }
                 }
             }
         }
@@ -2058,8 +2067,8 @@
                                 sInfo.applicationInfo.uid, name.getPackageName(),
                                 name.getClassName());
                     }
-                    r = new ServiceRecord(mAm, ss, className, name, filter, sInfo, callingFromFg,
-                            res);
+                    r = new ServiceRecord(mAm, ss, className, name, filter, sInfo,
+                            callingFromFg, res);
                     res.setService(r);
                     smap.mServicesByInstanceName.put(name, r);
                     smap.mServicesByIntent.put(filter, r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 562e80d..c16f1db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
 import android.os.WorkSource;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.sysprop.VoldProperties;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.style.SuggestionSpan;
@@ -4807,8 +4808,8 @@
             SystemProperties.set("sys.boot_completed", "1");
 
             // And trigger dev.bootcomplete if we are not showing encryption progress
-            if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
-                    || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
+            if (!"trigger_restart_min_framework".equals(VoldProperties.decrypt().orElse(""))
+                    || "".equals(VoldProperties.encrypt_progress().orElse(""))) {
                 SystemProperties.set("dev.bootcomplete", "1");
             }
             mUserController.sendBootCompleted(
@@ -9208,6 +9209,19 @@
             }
             sdumper.dumpWithClient();
         }
+        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 */);
+        }
         synchronized(this) {
             pw.println();
             if (dumpAll) {
@@ -9273,19 +9287,6 @@
             }
             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 */);
-        }
     }
 
     /**
@@ -10265,7 +10266,8 @@
                 if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                     continue;
                 }
-                r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
+                r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS, mProcessList.mLruProcesses.indexOf(r)
+                );
                 if (r.isPersistent()) {
                     numPers++;
                 }
@@ -10277,7 +10279,9 @@
             if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                 continue;
             }
-            r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS);
+            r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS,
+                    mProcessList.mLruProcesses.indexOf(r)
+            );
         }
 
         for (int i=0; i<mActiveInstrumentation.size(); i++) {
@@ -10375,7 +10379,7 @@
 
         writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
         mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
-        mAtmInternal.writeProcessesToProto(proto, dumpPackage);
+        mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode);
 
         if (dumpPackage == null) {
             mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
@@ -10405,14 +10409,6 @@
             }
         }
 
-        if (dumpPackage == null) {
-            final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
-            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
-                    PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
-            proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
-            proto.end(sleepToken);
-        }
-
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
             if (dumpPackage == null || dumpPackage.equals(mDebugApp)
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 62f1009..8cf9f1e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -537,40 +537,44 @@
     }
 
     private static String buildOomTag(String prefix, String space, int val, int base) {
-        if (val == base) {
+        final int diff = val - base;
+        if (diff == 0) {
             if (space == null) return prefix;
-            return prefix + "  ";
+            return prefix + space;
         }
-        return prefix + "+" + Integer.toString(val - base);
+        if (diff < 10) {
+            return prefix + "+ " + Integer.toString(diff);
+        }
+        return prefix + "+" + Integer.toString(diff);
     }
 
     public static String makeOomAdjString(int setAdj) {
         if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-            return buildOomTag("cch", "  ", setAdj, ProcessList.CACHED_APP_MIN_ADJ);
+            return buildOomTag("cch", "   ", setAdj, ProcessList.CACHED_APP_MIN_ADJ);
         } else if (setAdj >= ProcessList.SERVICE_B_ADJ) {
-            return buildOomTag("svcb ", null, setAdj, ProcessList.SERVICE_B_ADJ);
+            return buildOomTag("svcb  ", null, setAdj, ProcessList.SERVICE_B_ADJ);
         } else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
-            return buildOomTag("prev ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ);
+            return buildOomTag("prev  ", null, setAdj, ProcessList.PREVIOUS_APP_ADJ);
         } else if (setAdj >= ProcessList.HOME_APP_ADJ) {
-            return buildOomTag("home ", null, setAdj, ProcessList.HOME_APP_ADJ);
+            return buildOomTag("home  ", null, setAdj, ProcessList.HOME_APP_ADJ);
         } else if (setAdj >= ProcessList.SERVICE_ADJ) {
-            return buildOomTag("svc  ", null, setAdj, ProcessList.SERVICE_ADJ);
+            return buildOomTag("svc   ", null, setAdj, ProcessList.SERVICE_ADJ);
         } else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
-            return buildOomTag("hvy  ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
+            return buildOomTag("hvy   ", null, setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
         } else if (setAdj >= ProcessList.BACKUP_APP_ADJ) {
-            return buildOomTag("bkup ", null, setAdj, ProcessList.BACKUP_APP_ADJ);
+            return buildOomTag("bkup  ", null, setAdj, ProcessList.BACKUP_APP_ADJ);
         } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
-            return buildOomTag("prcp ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
+            return buildOomTag("prcp  ", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
         } else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) {
-            return buildOomTag("vis  ", null, setAdj, ProcessList.VISIBLE_APP_ADJ);
+            return buildOomTag("vis", "   ", setAdj, ProcessList.VISIBLE_APP_ADJ);
         } else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) {
-            return buildOomTag("fore ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ);
+            return buildOomTag("fore  ", null, setAdj, ProcessList.FOREGROUND_APP_ADJ);
         } else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
-            return buildOomTag("psvc ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ);
+            return buildOomTag("psvc  ", null, setAdj, ProcessList.PERSISTENT_SERVICE_ADJ);
         } else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
-            return buildOomTag("pers ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ);
+            return buildOomTag("pers  ", null, setAdj, ProcessList.PERSISTENT_PROC_ADJ);
         } else if (setAdj >= ProcessList.SYSTEM_ADJ) {
-            return buildOomTag("sys  ", null, setAdj, ProcessList.SYSTEM_ADJ);
+            return buildOomTag("sys   ", null, setAdj, ProcessList.SYSTEM_ADJ);
         } else if (setAdj >= ProcessList.NATIVE_ADJ) {
             return buildOomTag("ntv  ", null, setAdj, ProcessList.NATIVE_ADJ);
         } else {
@@ -2237,6 +2241,191 @@
         return index;
     }
 
+    /**
+     * Handle the case where we are inserting a process hosting client activities:
+     * Make sure any groups have their order match their importance, and take care of
+     * distributing old clients across other activity processes so they can't spam
+     * the LRU list.  Processing of the list will be restricted by the indices provided,
+     * and not extend out of them.
+     *
+     * @param topApp The app at the top that has just been inserted in to the list.
+     * @param topI The position in the list where topApp was inserted; this is the start (at the
+     *             top) where we are going to do our processing.
+     * @param bottomI The last position at which we will be processing; this is the end position
+     *                of whichever section of the LRU list we are in.  Nothing past it will be
+     *                touched.
+     * @param endIndex The current end of the top being processed.  Typically topI - 1.  That is,
+     *                 where we are going to start potentially adjusting other entries in the list.
+     */
+    private void updateClientActivitiesOrdering(final ProcessRecord topApp, final int topI,
+            final int bottomI, int endIndex) {
+        if (topApp.hasActivitiesOrRecentTasks() || topApp.treatLikeActivity
+                || !topApp.hasClientActivities()) {
+            // If this is not a special process that has client activities, then there is
+            // nothing to do.
+            return;
+        }
+
+        final int uid = topApp.info.uid;
+        if (topApp.connectionGroup > 0) {
+            int endImportance = topApp.connectionImportance;
+            for (int i = endIndex; i >= bottomI; i--) {
+                final ProcessRecord subProc = mLruProcesses.get(i);
+                if (subProc.info.uid == uid
+                        && subProc.connectionGroup == topApp.connectionGroup) {
+                    if (i == endIndex && subProc.connectionImportance >= endImportance) {
+                        // This process is already in the group, and its importance
+                        // is not as strong as the process before it, so keep it
+                        // correctly positioned in the group.
+                        if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                "Keeping in-place above " + subProc
+                                        + " endImportance=" + endImportance
+                                        + " group=" + subProc.connectionGroup
+                                        + " importance=" + subProc.connectionImportance);
+                        endIndex--;
+                        endImportance = subProc.connectionImportance;
+                    } else {
+                        // We want to pull this up to be with the rest of the group,
+                        // and order within the group by importance.
+                        if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                "Pulling up " + subProc
+                                        + " to position in group with importance="
+                                        + subProc.connectionImportance);
+                        boolean moved = false;
+                        for (int pos = topI; pos > endIndex; pos--) {
+                            final ProcessRecord posProc = mLruProcesses.get(pos);
+                            if (subProc.connectionImportance
+                                    <= posProc.connectionImportance) {
+                                mLruProcesses.remove(i);
+                                mLruProcesses.add(pos, subProc);
+                                if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                        "Moving " + subProc
+                                                + " from position " + i + " to above " + posProc
+                                                + " @ " + pos);
+                                moved = true;
+                                endIndex--;
+                                break;
+                            }
+                        }
+                        if (!moved) {
+                            // Goes to the end of the group.
+                            mLruProcesses.remove(i);
+                            mLruProcesses.add(endIndex - 1, subProc);
+                            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                    "Moving " + subProc
+                                            + " from position " + i + " to end of group @ "
+                                            + endIndex);
+                            endIndex--;
+                            endImportance = subProc.connectionImportance;
+                        }
+                    }
+                }
+            }
+
+        }
+        // To keep it from spamming the LRU list (by making a bunch of clients),
+        // we will distribute other entries owned by it to be in-between other apps.
+        int i = endIndex;
+        while (i >= bottomI) {
+            ProcessRecord subProc = mLruProcesses.get(i);
+            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                    "Looking to spread old procs, at " + subProc + " @ " + i);
+            if (subProc.info.uid != uid) {
+                // This is a different app...  if we have gone through some of the
+                // target app, pull this up to be before them.  We want to pull up
+                // one activity process, but any number of non-activity processes.
+                if (i < endIndex) {
+                    boolean hasActivity = false;
+                    int connUid = 0;
+                    int connGroup = 0;
+                    while (i >= bottomI) {
+                        mLruProcesses.remove(i);
+                        mLruProcesses.add(endIndex, subProc);
+                        if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                "Different app, moving to " + endIndex);
+                        i--;
+                        if (i < bottomI) {
+                            break;
+                        }
+                        subProc = mLruProcesses.get(i);
+                        if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                "Looking at next app at " + i + ": " + subProc);
+                        if (subProc.hasActivitiesOrRecentTasks() || subProc.treatLikeActivity) {
+                            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                    "This is hosting an activity!");
+                            if (hasActivity) {
+                                // Already found an activity, done.
+                                if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                        "Already found an activity, done");
+                                break;
+                            }
+                            hasActivity = true;
+                        } else if (subProc.hasClientActivities()) {
+                            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                    "This is a client of an activity");
+                            if (hasActivity) {
+                                if (connUid == 0 || connUid != subProc.info.uid) {
+                                    // Already have an activity that is not from from a client
+                                    // connection or is a different client connection, done.
+                                    if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                            "Already found a different activity: connUid="
+                                            + connUid + " uid=" + subProc.info.uid);
+                                    break;
+                                } else if (connGroup == 0 || connGroup != subProc.connectionGroup) {
+                                    // Previously saw a different group or not from a group,
+                                    // want to treat these as different things.
+                                    if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                            "Already found a different group: connGroup="
+                                            + connGroup + " group=" + subProc.connectionGroup);
+                                    break;
+                                }
+                            } else {
+                                if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                        "This is an activity client!  uid="
+                                        + subProc.info.uid + " group=" + subProc.connectionGroup);
+                                hasActivity = true;
+                                connUid = subProc.info.uid;
+                                connGroup = subProc.connectionGroup;
+                            }
+                        }
+                        endIndex--;
+                    }
+                }
+                // Find the end of the next group of processes for target app.  This
+                // is after any entries of different apps (so we don't change the existing
+                // relative order of apps) and then after the next last group of processes
+                // of the target app.
+                for (endIndex--; endIndex >= bottomI; endIndex--) {
+                    final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                    if (endProc.info.uid == uid) {
+                        if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                "Found next group of app: " + endProc + " @ "
+                                        + endIndex);
+                        break;
+                    }
+                }
+                if (endIndex >= bottomI) {
+                    final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                    for (endIndex--; endIndex >= bottomI; endIndex--) {
+                        final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+                        if (nextEndProc.info.uid != uid
+                                || nextEndProc.connectionGroup != endProc.connectionGroup) {
+                            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                    "Found next group or app: " + nextEndProc + " @ "
+                                            + endIndex + " group=" + nextEndProc.connectionGroup);
+                            break;
+                        }
+                    }
+                }
+                if (DEBUG_LRU) Slog.d(TAG_LRU,
+                        "Bumping scan position to " + endIndex);
+                i = endIndex;
+            } else {
+                i--;
+            }
+        }
+    }
+
     final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
             ProcessRecord client) {
         final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
@@ -2349,91 +2538,31 @@
             if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
                     && mLruProcessActivityStart < (N - 1)) {
                 // Process doesn't have activities, but has clients with
-                // activities...  move it up, but one below the top (the top
-                // should always have a real activity).
+                // activities...  move it up, but below the app that is binding to it.
                 if (DEBUG_LRU) Slog.d(TAG_LRU,
-                        "Adding to second-top of LRU activity list: " + app);
-                mLruProcesses.add(N - 1, app);
+                        "Adding to second-top of LRU activity list: " + app
+                        + " group=" + app.connectionGroup
+                        + " importance=" + app.connectionImportance);
+                int pos = N - 1;
+                while (pos > mLruProcessActivityStart) {
+                    final ProcessRecord posproc = mLruProcesses.get(pos);
+                    if (posproc.info.uid == app.info.uid) {
+                        // Technically this app could have multiple processes with different
+                        // activities and so we should be looking for the actual process that
+                        // is bound to the target proc...  but I don't really care, do you?
+                        break;
+                    }
+                    pos--;
+                }
+                mLruProcesses.add(pos, app);
                 // If this process is part of a group, need to pull up any other processes
                 // in that group to be with it.
-                final int uid = app.info.uid;
-                int endIndex = N - 2;
-                nextActivityIndex = N - 2;
-                if (app.connectionGroup > 0) {
-                    int endImportance = app.connectionImportance;
-                    for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
-                        final ProcessRecord subProc = mLruProcesses.get(i);
-                        if (subProc.info.uid == uid
-                                && subProc.connectionGroup == subProc.connectionGroup) {
-                            if (i == endIndex && subProc.connectionImportance >= endImportance) {
-                                // This process is already in the group, and its importance
-                                // is not as strong as the process before it, so it keep it
-                                // correctly positioned in the group.
-                                endIndex--;
-                                endImportance = subProc.connectionImportance;
-                            } else {
-                                // We want to pull this up to be with the rest of the group,
-                                // and order within the group by importance.
-                                boolean moved = false;
-                                for (int pos = N - 1; pos > endIndex; pos--) {
-                                    final ProcessRecord posProc = mLruProcesses.get(pos);
-                                    if (subProc.connectionImportance
-                                            <= posProc.connectionImportance) {
-                                        mLruProcesses.remove(i);
-                                        mLruProcesses.add(pos, subProc);
-                                        moved = true;
-                                        endIndex--;
-                                        break;
-                                    }
-                                }
-                                if (!moved) {
-                                    // Goes to the end of the group.
-                                    mLruProcesses.remove(i);
-                                    mLruProcesses.add(endIndex - 1, subProc);
-                                    endIndex--;
-                                    endImportance = subProc.connectionImportance;
-                                }
-                            }
-                        }
-                    }
-
+                int endIndex = pos - 1;
+                if (endIndex < mLruProcessActivityStart) {
+                    endIndex = mLruProcessActivityStart;
                 }
-                // To keep it from spamming the LRU list (by making a bunch of clients),
-                // we will distribute other entries owned by it to be in-between other apps.
-                for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
-                    final ProcessRecord subProc = mLruProcesses.get(i);
-                    if (subProc.info.uid != uid) {
-                        // This is a different app...  if we have gone through some of the
-                        // target app, pull this up to be before them.
-                        if (i < endIndex) {
-                            mLruProcesses.remove(i);
-                            mLruProcesses.add(endIndex, subProc);
-                        }
-                        // Find the end of the next group of processes for target app.  This
-                        // is after any entries of different apps (so we don't change the existing
-                        // relative order of apps) and then after the next last group of processes
-                        // of the target app.
-                        for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
-                            final ProcessRecord endProc = mLruProcesses.get(endIndex);
-                            if (endProc.info.uid == uid) {
-                                break;
-                            }
-                        }
-                        if (endIndex >= mLruProcessActivityStart) {
-                            final ProcessRecord endProc = mLruProcesses.get(endIndex);
-                            for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
-                                final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
-                                if (nextEndProc.info.uid != uid
-                                        || nextEndProc.connectionGroup != endProc.connectionGroup) {
-                                    break;
-                                }
-                            }
-                        }
-                        if (i > endIndex) {
-                            i = endIndex;
-                        }
-                    }
-                }
+                nextActivityIndex = endIndex;
+                updateClientActivitiesOrdering(app, pos, mLruProcessActivityStart, endIndex);
             } else {
                 // Process has activities, put it at the very tipsy-top.
                 if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
@@ -2469,6 +2598,9 @@
             nextIndex = index - 1;
             mLruProcessActivityStart++;
             mLruProcessServiceStart++;
+            if (index > 1) {
+                updateClientActivitiesOrdering(app, mLruProcessServiceStart - 1, 0, index - 1);
+            }
         }
 
         app.lruSeq = mLruSeq;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 013de93..4826f48 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -586,7 +586,7 @@
                 }
                 origBase.makeInactive();
             }
-            baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
+            baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
                     info.longVersionCode, processName);
             baseProcessTracker.makeActive();
             for (int i=0; i<pkgList.size(); i++) {
@@ -594,7 +594,7 @@
                 if (holder.state != null && holder.state != origBase) {
                     holder.state.makeInactive();
                 }
-                tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), uid,
+                tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
                         info.longVersionCode, processName);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
@@ -760,19 +760,25 @@
 
     @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        writeToProto(proto, fieldId, -1);
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId, int lruIndex) {
         long token = proto.start(fieldId);
         proto.write(ProcessRecordProto.PID, pid);
         proto.write(ProcessRecordProto.PROCESS_NAME, processName);
-        if (info.uid < Process.FIRST_APPLICATION_UID) {
-            proto.write(ProcessRecordProto.UID, uid);
-        } else {
+        proto.write(ProcessRecordProto.UID, info.uid);
+        if (UserHandle.getAppId(info.uid) >= Process.FIRST_APPLICATION_UID) {
             proto.write(ProcessRecordProto.USER_ID, userId);
             proto.write(ProcessRecordProto.APP_ID, UserHandle.getAppId(info.uid));
-            if (uid != info.uid) {
-                proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
-            }
+        }
+        if (uid != info.uid) {
+            proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
         }
         proto.write(ProcessRecordProto.PERSISTENT, mPersistent);
+        if (lruIndex >= 0) {
+            proto.write(ProcessRecordProto.LRU_INDEX, lruIndex);
+        }
         proto.end(token);
     }
 
@@ -864,7 +870,8 @@
             ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                     versionCode);
             if (baseProcessTracker != null) {
-                tracker.updateProcessStateHolderLocked(holder, pkg, uid, versionCode, processName);
+                tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
+                        processName);
                 pkgList.put(pkg, holder);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
@@ -925,7 +932,7 @@
                 pkgList.clear();
                 ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                         info.longVersionCode);
-                tracker.updateProcessStateHolderLocked(holder, info.packageName, uid,
+                tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
                         info.longVersionCode, processName);
                 pkgList.put(info.packageName, holder);
                 if (holder.state != baseProcessTracker) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 09f8c3e..da5ce1c 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -121,6 +121,8 @@
     long nextRestartTime;   // time when restartDelay will expire.
     boolean destroying;     // set when we have started destroying the service
     long destroyTime;       // time at which destory was initiated.
+    int pendingConnectionGroup;        // To be filled in to ProcessRecord once it connects
+    int pendingConnectionImportance;   // To be filled in to ProcessRecord once it connects
 
     String stringName;      // caching of toString
 
@@ -386,6 +388,11 @@
                 pw.print(" restartTime=");
                 TimeUtils.formatDuration(restartTime, now, pw);
                 pw.print(" createdFromFg="); pw.println(createdFromFg);
+        if (pendingConnectionGroup != 0) {
+            pw.print(prefix); pw.print(" pendingConnectionGroup=");
+            pw.print(pendingConnectionGroup);
+            pw.print(" Importance="); pw.println(pendingConnectionImportance);
+        }
         if (startRequested || delayedStop || lastStartId != 0) {
             pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
                     pw.print(" delayedStop="); pw.print(delayedStop);
@@ -461,7 +468,11 @@
         serviceInfo = sInfo;
         appInfo = sInfo.applicationInfo;
         packageName = sInfo.applicationInfo.packageName;
-        processName = sInfo.processName;
+        if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+            processName = sInfo.processName + ":" + instanceName.getClassName();
+        } else {
+            processName = sInfo.processName;
+        }
         permission = sInfo.permission;
         exported = sInfo.exported;
         this.restarter = restarter;
@@ -507,6 +518,12 @@
 
     public void setProcess(ProcessRecord _proc) {
         app = _proc;
+        if (pendingConnectionGroup > 0) {
+            app.connectionService = this;
+            app.connectionGroup = pendingConnectionGroup;
+            app.connectionImportance = pendingConnectionImportance;
+            pendingConnectionGroup = pendingConnectionImportance = 0;
+        }
         if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
             for (int conni = connections.size() - 1; conni >= 0; conni--) {
                 ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 353f787..271b37e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1144,7 +1144,7 @@
     /**
      * Attempt to unlock user without a credential token. This typically
      * succeeds when the device doesn't have credential-encrypted storage, or
-     * when the the credential-encrypted storage isn't tied to a user-provided
+     * when the credential-encrypted storage isn't tied to a user-provided
      * PIN or pattern.
      */
     private boolean maybeUnlockUser(final int userId) {
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 0b6786c..521fa23 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TypeEvaluator;
@@ -29,8 +31,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.hardware.display.IColorDisplayManager;
 import android.net.Uri;
 import android.opengl.Matrix;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -39,6 +43,7 @@
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
 
+import com.android.internal.R;
 import com.android.internal.app.ColorDisplayController;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
@@ -49,12 +54,8 @@
 import java.time.LocalTime;
 import java.time.ZoneId;
 
-import com.android.internal.R;
-
-import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
-
 /**
- * Tints the display at night.
+ * Controls the display's color transforms.
  */
 public final class ColorDisplayService extends SystemService
         implements ColorDisplayController.Callback {
@@ -101,7 +102,7 @@
 
     @Override
     public void onStart() {
-        // Nothing to publish.
+        publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
     }
 
     @Override
@@ -171,7 +172,7 @@
                     }
                 };
                 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
-                        false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser);
+                        false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
             } else if (mBootCompleted) {
                 setUp();
             }
@@ -405,8 +406,8 @@
     }
 
     /**
-     * Returns the first date time corresponding to the local time that occurs before the
-     * provided date time.
+     * Returns the first date time corresponding to the local time that occurs before the provided
+     * date time.
      *
      * @param compareTime the LocalDateTime to compare against
      * @return the prior LocalDateTime corresponding to this local time
@@ -420,8 +421,8 @@
     }
 
     /**
-     * Returns the first date time corresponding to this local time that occurs after the
-     * provided date time.
+     * Returns the first date time corresponding to this local time that occurs after the provided
+     * date time.
      *
      * @param compareTime the LocalDateTime to compare against
      * @return the next LocalDateTime corresponding to this local time
@@ -434,6 +435,11 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
+    private boolean isDeviceColorManagedInternal() {
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        return dtm.isDeviceColorManaged();
+    }
+
     private abstract class AutoMode implements ColorDisplayController.Callback {
         public abstract void onStart();
 
@@ -616,4 +622,16 @@
             return mResultMatrix;
         }
     }
+
+    private final class BinderService extends IColorDisplayManager.Stub {
+        @Override
+        public boolean isDeviceColorManaged() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return isDeviceColorManagedInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index d6931e0..5ca1755 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server.display;
 
-import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.opengl.Matrix;
 import android.os.IBinder;
@@ -27,8 +26,10 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ColorDisplayController;
+
 import java.util.Arrays;
 
 /**
@@ -59,10 +60,6 @@
 
     private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
     private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
-
-    private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
-    private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
-
     /**
      * SurfaceFlinger global saturation factor.
      */
@@ -71,6 +68,10 @@
      * SurfaceFlinger display color (managed, unmanaged, etc.).
      */
     private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
+    private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1030;
+
+    private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
+    private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
 
     private static final float COLOR_SATURATION_NATURAL = 1.0f;
     private static final float COLOR_SATURATION_BOOSTED = 1.1f;
@@ -269,6 +270,29 @@
     }
 
     /**
+     * Returns whether the screen is wide color gamut via SurfaceFlinger's
+     * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR}.
+     */
+    public boolean isDeviceColorManaged() {
+        final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
+        if (flinger != null) {
+            final Parcel data = Parcel.obtain();
+            final Parcel reply = Parcel.obtain();
+            data.writeInterfaceToken("android.ui.ISurfaceComposer");
+            try {
+                flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0);
+                return reply.readBoolean();
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to query wide color support", ex);
+            } finally {
+                data.recycle();
+                reply.recycle();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Propagates the provided saturation to the SurfaceFlinger.
      */
     private void applySaturation(float saturation) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b148a2f..c0d3fdf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -980,7 +980,7 @@
      * @param sourceAddress a logical address of source device where sends polling message
      * @param pickStrategy strategy how to pick polling candidates
      * @param retryCount the number of retry used to send polling message to remote devices
-     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
+     * @throws IllegalArgumentException if {@code pickStrategy} is invalid value
      */
     @ServiceThreadOnly
     void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
similarity index 81%
rename from services/core/java/com/android/server/AbstractMasterSystemService.java
rename to services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 9c1e3cd..96095d8 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server;
+package com.android.server.infra;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -39,6 +39,9 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -93,6 +96,12 @@
     public boolean debug = false;
 
     /**
+     * Whether the service is allowed to bind to an instant-app.
+     */
+    @GuardedBy("mLock")
+    protected boolean mAllowInstantService;
+
+    /**
      * Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
      * be disabled through {@link UserManager}.
      */
@@ -176,6 +185,107 @@
     }
 
     /**
+     * Gets whether the service is allowed to bind to an instant-app.
+     *
+     * <p>Typically called by {@code ShellCommand} during CTS tests.
+     *
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    public final boolean getAllowInstantService() {
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            return mAllowInstantService;
+        }
+    }
+
+    /**
+     * Sets whether the service is allowed to bind to an instant-app.
+     *
+     * <p>Typically called by {@code ShellCommand} during CTS tests.
+     *
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    public final void setAllowInstantService(boolean mode) {
+        Slog.i(mTag, "setAllowInstantService(): " + mode);
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            mAllowInstantService = mode;
+        }
+    }
+
+    /**
+     * Temporary sets the service implementation.
+     *
+     * <p>Typically used by Shell command and/or CTS tests.
+     *
+     * @param componentName name of the new component
+     * @param durationMs how long the change will be valid (the service will be automatically reset
+     *            to the default component after this timeout expires).
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+     *             {@link #getMaximumTemporaryServiceDurationMs()}.
+     */
+    public final void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+            int durationMs) {
+        Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
+                + durationMs + "ms");
+        enforceCallingPermissionForManagement();
+
+        Preconditions.checkNotNull(componentName);
+        final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+        if (durationMs > maxDurationMs) {
+            throw new IllegalArgumentException(
+                    "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+        }
+
+        synchronized (mLock) {
+            final S service = getServiceForUserLocked(userId);
+            if (service != null) {
+                service.setTemporaryServiceLocked(componentName, durationMs);
+            }
+        }
+    }
+
+    /**
+     * Gets the maximum time the service implementation can be changed.
+     *
+     * @throws UnsupportedOperationException if subclass doesn't override it.
+     */
+    protected int getMaximumTemporaryServiceDurationMs() {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
+
+    /**
+     * Resets the temporary service implementation to the default component.
+     *
+     * <p>Typically used by Shell command and/or CTS tests.
+     *
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    public final void resetTemporaryService(@UserIdInt int userId) {
+        Slog.i(mTag, "resetTemporaryService(): " + userId);
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            final S service = getServiceForUserLocked(userId);
+            if (service != null) {
+                service.resetTemporaryServiceLocked();
+            }
+        }
+    }
+
+    /**
+     * Asserts that the caller has permissions to manage this service.
+     *
+     * <p>Typically called by {@code ShellCommand} implementations.
+     *
+     * @throws UnsupportedOperationException if subclass doesn't override it.
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    protected void enforceCallingPermissionForManagement() {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
+
+    /**
      * Creates a new service that will be added to the cache.
      *
      * @param resolvedUserId the resolved user id for the service.
@@ -362,6 +472,7 @@
             pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
             pw.print(" Verbose: "); pw.println(realVerbose);
             pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
+            pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
             pw.print(prefix); pw.print("Settings property: "); pw.println(
                     getServiceSettingsProperty());
             pw.print(prefix); pw.print("Cached services: ");
diff --git a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
similarity index 98%
rename from services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
index f532b22..513a6a3 100644
--- a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.infra;
 
 import android.annotation.NonNull;
 import android.content.ComponentName;
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
similarity index 68%
rename from services/core/java/com/android/server/AbstractPerUserSystemService.java
rename to services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 001d85f..dfe8a36 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server;
+package com.android.server.infra;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -26,12 +26,17 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -49,6 +54,9 @@
 public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
         M extends AbstractMasterSystemService<M, S>> {
 
+    /** Handler message to {@link #resetTemporaryServiceLocked()} */
+    private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+
     protected final @UserIdInt int mUserId;
     protected final Object mLock;
     protected final String mTag = getClass().getSimpleName();
@@ -70,6 +78,26 @@
     @GuardedBy("mLock")
     private ServiceInfo mServiceInfo;
 
+    /**
+     * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
+     *
+     * <p>Typically used by Shell command and/or CTS tests.
+     */
+    @GuardedBy("mLock")
+    private String mTemporaryServiceName;
+
+    /**
+     * When the temporary service will expire (and reset back to the default).
+     */
+    @GuardedBy("mLock")
+    private long mTemporaryServiceExpiration;
+
+    /**
+     * Handler used to reset the temporary service name.
+     */
+    @GuardedBy("mLock")
+    private Handler mTemporaryHandler;
+
     protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
             @UserIdInt int userId) {
         mMaster = master;
@@ -130,7 +158,7 @@
         mDisabled = disabled;
         ComponentName serviceComponent = null;
         ServiceInfo serviceInfo = null;
-        final String componentName = getComponentNameFromSettings();
+        final String componentName = getComponentNameLocked();
         if (!TextUtils.isEmpty(componentName)) {
             try {
                 serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -191,6 +219,29 @@
     }
 
     /**
+     * Gets the current name of the service, which is either the
+     *  {@link #getDefaultComponentName() default service} or the
+     *  {@link #setTemporaryServiceLocked(String, int) temporary one}.
+     */
+    protected final String getComponentNameLocked() {
+        if (mTemporaryServiceName != null) {
+            // Always log it, as it should only be used on CTS or during development
+            Slog.w(mTag, "getComponentName(): using temporary name " + mTemporaryServiceName);
+            return mTemporaryServiceName;
+        }
+        return getDefaultComponentName();
+    }
+
+    /**
+     * Gets the name of the default component for the service.
+     *
+     * <p>Typically implemented by returning {@link #getComponentNameFromSettings()} or by using
+     * a string from the system resources.
+     */
+    @Nullable
+    protected abstract String getDefaultComponentName();
+
+    /**
      * Gets this name of the remote service this service binds to as defined by {@link Settings}.
      */
     @Nullable
@@ -201,6 +252,66 @@
     }
 
     /**
+     * Checks whether the current service for the user was temporarily set.
+     */
+    public final boolean isTemporaryServiceSetLocked() {
+        return mTemporaryServiceName != null;
+    }
+
+    /**
+     * Temporary sets the service implementation.
+     *
+     * @param componentName name of the new component
+     * @param durationMs how long the change will be valid (the service will be automatically reset
+     * to the default component after this timeout expires).
+     */
+    protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+        mTemporaryServiceName = componentName;
+
+        if (mTemporaryHandler == null) {
+            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+                @Override
+                public void handleMessage(Message msg) {
+                    if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+                        synchronized (mLock) {
+                            resetTemporaryServiceLocked();
+                        }
+                    } else {
+                        Slog.wtf(mTag, "invalid handler msg: " + msg);
+                    }
+                }
+            };
+        } else {
+            removeResetTemporaryServiceMessageLocked();
+        }
+        mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+        mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+
+        updateLocked(mDisabled);
+    }
+
+    private void removeResetTemporaryServiceMessageLocked() {
+        if (mMaster.verbose) {
+            Slog.v(mTag, "setTemporaryServiceLocked(): removing old message");
+        }
+        // NOTE: caller should already have checked it
+        mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+    }
+
+    /**
+     * Resets the temporary service implementation to the default component.
+     */
+    protected final void resetTemporaryServiceLocked() {
+        Slog.i(mTag, "resetting temporary service from " + mTemporaryServiceName);
+        mTemporaryServiceName = null;
+        if (mTemporaryHandler != null) {
+            removeResetTemporaryServiceMessageLocked();
+            mTemporaryHandler = null;
+        }
+        updateLocked(mDisabled);
+    }
+
+    /**
      * Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
      * if the service is disabled.
      */
@@ -290,12 +401,14 @@
             pw.print(prefix); pw.print("Service UID: ");
             pw.println(mServiceInfo.applicationInfo.uid);
         }
-        final String componentName = getComponentNameFromSettings();
-        if (componentName != null) {
-            pw.print(prefix); pw.print("Service name: ");
-            pw.println(componentName);
+        if (mTemporaryServiceName != null) {
+            pw.print(prefix); pw.print("Temporary service name: "); pw.print(mTemporaryServiceName);
+            final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+            pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.println(")");
+            pw.print(prefix); pw.print(prefix);
+            pw.print("Default service name: "); pw.println(getDefaultComponentName());
         } else {
-            pw.println("No service package set");
+            pw.print(prefix); pw.print("Service name: "); pw.println(getDefaultComponentName());
         }
     }
 }
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/infra/AbstractRemoteService.java
similarity index 99%
rename from services/core/java/com/android/server/AbstractRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractRemoteService.java
index f636487..7af1d4c 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractRemoteService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.infra;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
@@ -33,6 +33,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.FgThread;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
diff --git a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
similarity index 98%
rename from services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
rename to services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
index 8e1f540..8f8b448 100644
--- a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
+++ b/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.infra;
 
 import android.annotation.NonNull;
 import android.content.ComponentName;
diff --git a/services/core/java/com/android/server/infra/package.html b/services/core/java/com/android/server/infra/package.html
new file mode 100644
index 0000000..61a4c43
--- /dev/null
+++ b/services/core/java/com/android/server/infra/package.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+Contains common classes providing the plumbing infrastructure necessary to implement a system
+service
+</body>
+</html>
\ No newline at end of file
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index d5be26a..f424869 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -16,10 +16,12 @@
 package com.android.server.intelligence;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
 
 /**
@@ -53,6 +55,7 @@
      * @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.
+     * @param focusedValue current value of 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
@@ -60,7 +63,8 @@
      */
     public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
             @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
-            int autofillSessionId, @NonNull AutofillId focusedId);
+            int autofillSessionId, @NonNull AutofillId focusedId,
+            @Nullable AutofillValue focusedValue);
 
     /**
      * Callback used by the Autofill Session to communicate with the Augmented Autofill service.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b3f0629..ea295de 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -78,7 +78,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
@@ -97,6 +96,7 @@
 import com.android.server.job.controllers.DeviceIdleJobsController;
 import com.android.server.job.controllers.IdleController;
 import com.android.server.job.controllers.JobStatus;
+import com.android.server.job.controllers.QuotaController;
 import com.android.server.job.controllers.StateController;
 import com.android.server.job.controllers.StorageController;
 import com.android.server.job.controllers.TimeController;
@@ -245,11 +245,11 @@
      * Named indices into the STANDBY_BEATS array, for clarity in referring to
      * specific buckets' bookkeeping.
      */
-    static final int ACTIVE_INDEX = 0;
-    static final int WORKING_INDEX = 1;
-    static final int FREQUENT_INDEX = 2;
-    static final int RARE_INDEX = 3;
-    static final int NEVER_INDEX = 4;
+    public static final int ACTIVE_INDEX = 0;
+    public static final int WORKING_INDEX = 1;
+    public static final int FREQUENT_INDEX = 2;
+    public static final int RARE_INDEX = 3;
+    public static final int NEVER_INDEX = 4;
 
     /**
      * Bookkeeping about when jobs last run.  We keep our own record in heartbeat time,
@@ -308,6 +308,10 @@
                 try {
                     mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
                             Settings.Global.JOB_SCHEDULER_CONSTANTS));
+                    for (int controller = 0; controller < mControllers.size(); controller++) {
+                        final StateController sc = mControllers.get(controller);
+                        sc.onConstantsUpdatedLocked();
+                    }
                 } catch (IllegalArgumentException e) {
                     // Failed to parse the settings string, log this and move on
                     // with defaults.
@@ -315,8 +319,10 @@
                 }
             }
 
-            // Reset the heartbeat alarm based on the new heartbeat duration
-            setNextHeartbeatAlarm();
+            if (mConstants.USE_HEARTBEATS) {
+                // Reset the heartbeat alarm based on the new heartbeat duration
+                setNextHeartbeatAlarm();
+            }
         }
     }
 
@@ -352,6 +358,19 @@
         private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
+        private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
+        private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
+                "qc_allowed_time_per_period_ms";
+        private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+                "qc_in_quota_buffer_ms";
+        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
+                "qc_window_size_active_ms";
+        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
+                "qc_window_size_working_ms";
+        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
+                "qc_window_size_frequent_ms";
+        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
+                "qc_window_size_rare_ms";
 
         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -377,6 +396,19 @@
         private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
+        private static final boolean DEFAULT_USE_HEARTBEATS = true;
+        private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+                30 * 1000L; // 30 seconds
+        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
+                10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
+        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
+                2 * 60 * 60 * 1000L; // 2 hours
+        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
+                8 * 60 * 60 * 1000L; // 8 hours
+        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
+                24 * 60 * 60 * 1000L; // 24 hours
 
         /**
          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -495,6 +527,54 @@
          * we consider matching it against a metered network.
          */
         public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
+        /**
+         * Whether to use heartbeats or rolling window for quota management. True will use
+         * heartbeats, false will use a rolling window.
+         */
+        public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
+
+        /** How much time each app will have to run jobs within their standby bucket window. */
+        public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
+                DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
+
+        /**
+         * How much time the package should have before transitioning from out-of-quota to in-quota.
+         * This should not affect processing if the package is already in-quota.
+         */
+        public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
+                DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
+                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
+                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
+                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
+                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -567,6 +647,25 @@
                     DEFAULT_CONN_CONGESTION_DELAY_FRAC);
             CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
+            USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
+            QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
+                    DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
+            QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
+                    DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
+            QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
+                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
+            QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
+                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
+            QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
+                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
+            QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
+                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
+                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
         }
 
         void dump(IndentingPrintWriter pw) {
@@ -600,6 +699,19 @@
             pw.println('}');
             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
             pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
+            pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
+                    QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
+                    QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
             pw.decreaseIndent();
         }
 
@@ -629,6 +741,23 @@
             }
             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
+            proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
+
+            final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
+            proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
+                    QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
+            proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS,
+                    QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
+            proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
+            proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
+            proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
+            proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
+                    QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
+            proto.end(qcToken);
+
             proto.end(token);
         }
     }
@@ -1162,6 +1291,7 @@
         mControllers.add(new ContentObserverController(this));
         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
         mControllers.add(mDeviceIdleJobsController);
+        mControllers.add(new QuotaController(this));
 
         // If the job store determined that it can't yet reschedule persisted jobs,
         // we need to start watching the clock.
@@ -1225,7 +1355,9 @@
 
             mAppStateTracker = Preconditions.checkNotNull(
                     LocalServices.getService(AppStateTracker.class));
-            setNextHeartbeatAlarm();
+            if (mConstants.USE_HEARTBEATS) {
+                setNextHeartbeatAlarm();
+            }
 
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
@@ -1869,6 +2001,9 @@
 
     // Intentionally does not touch the alarm timing
     void advanceHeartbeatLocked(long beatsElapsed) {
+        if (!mConstants.USE_HEARTBEATS) {
+            return;
+        }
         mHeartbeat += beatsElapsed;
         if (DEBUG_STANDBY) {
             Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
@@ -1904,6 +2039,9 @@
     void setNextHeartbeatAlarm() {
         final long heartbeatLength;
         synchronized (mLock) {
+            if (!mConstants.USE_HEARTBEATS) {
+                return;
+            }
             heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
         }
         final long now = sElapsedRealtimeClock.millis();
@@ -1976,48 +2114,51 @@
             return false;
         }
 
-        // If the app is in a non-active standby bucket, make sure we've waited
-        // an appropriate amount of time since the last invocation.  During device-
-        // wide parole, standby bucketing is ignored.
-        //
-        // Jobs in 'active' apps are not subject to standby, nor are jobs that are
-        // specifically marked as exempt.
-        if (DEBUG_STANDBY) {
-            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
-                    + " parole=" + mInParole + " active=" + job.uidActive
-                    + " exempt=" + job.getJob().isExemptedFromAppStandby());
-        }
-        if (!mInParole
-                && !job.uidActive
-                && !job.getJob().isExemptedFromAppStandby()) {
-            final int bucket = job.getStandbyBucket();
+        if (mConstants.USE_HEARTBEATS) {
+            // If the app is in a non-active standby bucket, make sure we've waited
+            // an appropriate amount of time since the last invocation.  During device-
+            // wide parole, standby bucketing is ignored.
+            //
+            // Jobs in 'active' apps are not subject to standby, nor are jobs that are
+            // specifically marked as exempt.
             if (DEBUG_STANDBY) {
-                Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
-                        + " next=" + mNextBucketHeartbeat[bucket]);
+                Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+                        + " parole=" + mInParole + " active=" + job.uidActive
+                        + " exempt=" + job.getJob().isExemptedFromAppStandby());
             }
-            if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
-                // Only skip this job if the app is still waiting for the end of its nominal
-                // bucket interval.  Once it's waited that long, we let it go ahead and clear.
-                // The final (NEVER) bucket is special; we never age those apps' jobs into
-                // runnability.
-                final long appLastRan = heartbeatWhenJobsLastRun(job);
-                if (bucket >= mConstants.STANDBY_BEATS.length
-                        || (mHeartbeat > appLastRan
-                                && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
-                    // TODO: log/trace that we're deferring the job due to bucketing if we hit this
-                    if (job.getWhenStandbyDeferred() == 0) {
-                        if (DEBUG_STANDBY) {
-                            Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
-                                    + (appLastRan + mConstants.STANDBY_BEATS[bucket])
-                                    + " for " + job);
+            if (!mInParole
+                    && !job.uidActive
+                    && !job.getJob().isExemptedFromAppStandby()) {
+                final int bucket = job.getStandbyBucket();
+                if (DEBUG_STANDBY) {
+                    Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
+                            + " next=" + mNextBucketHeartbeat[bucket]);
+                }
+                if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
+                    // Only skip this job if the app is still waiting for the end of its nominal
+                    // bucket interval.  Once it's waited that long, we let it go ahead and clear.
+                    // The final (NEVER) bucket is special; we never age those apps' jobs into
+                    // runnability.
+                    final long appLastRan = heartbeatWhenJobsLastRun(job);
+                    if (bucket >= mConstants.STANDBY_BEATS.length
+                            || (mHeartbeat > appLastRan
+                            && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
+                        // TODO: log/trace that we're deferring the job due to bucketing if we
+                        // hit this
+                        if (job.getWhenStandbyDeferred() == 0) {
+                            if (DEBUG_STANDBY) {
+                                Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
+                                        + (appLastRan + mConstants.STANDBY_BEATS[bucket])
+                                        + " for " + job);
+                            }
+                            job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
                         }
-                        job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
-                    }
-                    return false;
-                } else {
-                    if (DEBUG_STANDBY) {
-                        Slog.v(TAG, "Bucket deferred job aged into runnability at "
-                                + mHeartbeat + " : " + job);
+                        return false;
+                    } else {
+                        if (DEBUG_STANDBY) {
+                            Slog.v(TAG, "Bucket deferred job aged into runnability at "
+                                    + mHeartbeat + " : " + job);
+                        }
                     }
                 }
             }
@@ -2364,32 +2505,7 @@
         @Override
         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
                 boolean idle, int bucket, int reason) {
-            final int uid = mLocalPM.getPackageUid(packageName,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-            if (uid < 0) {
-                if (DEBUG_STANDBY) {
-                    Slog.i(TAG, "App idle state change for unknown app "
-                            + packageName + "/" + userId);
-                }
-                return;
-            }
-
-            final int bucketIndex = standbyBucketToBucketIndex(bucket);
-            // update job bookkeeping out of band
-            BackgroundThread.getHandler().post(() -> {
-                if (DEBUG_STANDBY) {
-                    Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
-                }
-                synchronized (mLock) {
-                    mJobs.forEachJobForSourceUid(uid, job -> {
-                        // double-check uid vs package name to disambiguate shared uids
-                        if (packageName.equals(job.getSourcePackageName())) {
-                            job.setStandbyBucket(bucketIndex);
-                        }
-                    });
-                    onControllerStateChanged();
-                }
-            });
+            // QuotaController handles this now.
         }
 
         @Override
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 35fc29e..6deecbd 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -77,6 +77,7 @@
     static final int CONSTRAINT_CONNECTIVITY = 1<<28;
     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
+    static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
 
     // Soft override: ignore constraints like time that don't affect API availability
@@ -192,6 +193,10 @@
      * Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
      */
     public static final int TRACKING_TIME = 1<<5;
+    /**
+     * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job.
+     */
+    public static final int TRACKING_QUOTA = 1 << 6;
 
     /**
      * Bit mask of controllers that are currently tracking the job.
@@ -291,6 +296,9 @@
      */
     private boolean mReadyNotRestrictedInBg;
 
+    /** The job is within its quota based on its standby bucket. */
+    private boolean mReadyWithinQuota;
+
     /** Provide a handle to the service that this job will be run on. */
     public int getServiceToken() {
         return callingUid;
@@ -675,7 +683,6 @@
         return baseHeartbeat;
     }
 
-    // Called only by the standby monitoring code
     public void setStandbyBucket(int newBucket) {
         standbyBucket = newBucket;
     }
@@ -876,22 +883,27 @@
         mPersistedUtcTimes = null;
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setChargingConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setBatteryNotLowConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setStorageNotLowConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setTimingDelayConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setDeadlineConstraintSatisfied(boolean state) {
         if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
             // The constraint was changed. Update the ready flag.
@@ -901,18 +913,22 @@
         return false;
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setIdleConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_IDLE, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setConnectivityConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setContentTriggerConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
         dozeWhitelisted = whitelisted;
         if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
@@ -923,6 +939,7 @@
         return false;
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
         if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
             // The constraint was changed. Update the ready flag.
@@ -932,6 +949,17 @@
         return false;
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
+    boolean setQuotaConstraintSatisfied(boolean state) {
+        if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyWithinQuota = state;
+            return true;
+        }
+        return false;
+    }
+
+    /** @return true if the state was changed, false otherwise. */
     boolean setUidActive(final boolean newActiveState) {
         if (newActiveState != uidActive) {
             uidActive = newActiveState;
@@ -940,6 +968,7 @@
         return false; /* unchanged */
     }
 
+    /** @return true if the constraint was changed, false otherwise. */
     boolean setConstraintSatisfied(int constraint, boolean state) {
         boolean old = (satisfiedConstraints&constraint) != 0;
         if (old == state) {
@@ -978,9 +1007,13 @@
      * @return Whether or not this job is ready to run, based on its requirements.
      */
     public boolean isReady() {
-        // Deadline constraint trumps other constraints (except for periodic jobs where deadline
-        // is an implementation detail. A periodic job should only run if its constraints are
-        // satisfied).
+        // Quota constraints trumps all other constraints.
+        if (!mReadyWithinQuota) {
+            return false;
+        }
+        // Deadline constraint trumps other constraints besides quota (except for periodic jobs
+        // where deadline is an implementation detail. A periodic job should only run if its
+        // constraints are satisfied).
         // DeviceNotDozing implicit constraint must be satisfied
         // NotRestrictedInBackground implicit constraint must be satisfied
         return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
@@ -1169,6 +1202,9 @@
         if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
             pw.print(" BACKGROUND_NOT_RESTRICTED");
         }
+        if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
+            pw.print(" WITHIN_QUOTA");
+        }
         if (constraints != 0) {
             pw.print(" [0x");
             pw.print(Integer.toHexString(constraints));
@@ -1205,6 +1241,9 @@
         if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
             proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING);
         }
+        if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_WITHIN_QUOTA);
+        }
     }
 
     private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
@@ -1237,6 +1276,13 @@
      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
      */
     String getBucketName() {
+        return bucketName(standbyBucket);
+    }
+
+    /**
+     * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
+     */
+    static String bucketName(int standbyBucket) {
         switch (standbyBucket) {
             case 0: return "ACTIVE";
             case 1: return "WORKING_SET";
@@ -1367,7 +1413,8 @@
             dumpConstraints(pw, satisfiedConstraints);
             pw.println();
             pw.print(prefix); pw.print("Unsatisfied constraints:");
-            dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints));
+            dumpConstraints(pw,
+                    ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
             pw.println();
             if (dozeWhitelisted) {
                 pw.print(prefix); pw.println("Doze whitelisted: true");
@@ -1375,6 +1422,9 @@
             if (uidActive) {
                 pw.print(prefix); pw.println("Uid: active");
             }
+            if (job.isExemptedFromAppStandby()) {
+                pw.print(prefix); pw.println("Is exempted from app standby");
+            }
         }
         if (trackingControllers != 0) {
             pw.print(prefix); pw.print("Tracking:");
@@ -1384,6 +1434,7 @@
             if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
             if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
             if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
+            if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA");
             pw.println();
         }
 
@@ -1546,8 +1597,11 @@
         if (full) {
             dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
             dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
-                    (requiredConstraints & ~satisfiedConstraints));
+                    ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
             proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted);
+            proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive);
+            proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY,
+                    job.isExemptedFromAppStandby());
         }
 
         // Tracking controllers
@@ -1575,6 +1629,10 @@
             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
                     JobStatusDumpProto.TRACKING_TIME);
         }
+        if ((trackingControllers & TRACKING_QUOTA) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_QUOTA);
+        }
 
         // Implicit constraints
         final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS);
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
new file mode 100644
index 0000000..f73ffac
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -0,0 +1,1299 @@
+/*
+ * 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.job.controllers;
+
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AlarmManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateControllerProto;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Controller that tracks whether a package has exceeded its standby bucket quota.
+ *
+ * Each job in each bucket is given 10 minutes to run within its respective time window. Active
+ * jobs can run indefinitely, working set jobs can run for 10 minutes within a 2 hour window,
+ * frequent jobs get to run 10 minutes in an 8 hour window, and rare jobs get to run 10 minutes in
+ * a 24 hour window. The windows are rolling, so as soon as a job would have some quota based on its
+ * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately
+ * applied to it.
+ *
+ * Test: atest com.android.server.job.controllers.QuotaControllerTest
+ */
+public final class QuotaController extends StateController {
+    private static final String TAG = "JobScheduler.Quota";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final long MINUTE_IN_MILLIS = 60 * 1000L;
+
+    private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
+    private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
+
+    /**
+     * A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
+     * associations.
+     */
+    private static class UserPackageMap<T> {
+        private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>();
+
+        public void add(int userId, @NonNull String packageName, @Nullable T obj) {
+            ArrayMap<String, T> data = mData.get(userId);
+            if (data == null) {
+                data = new ArrayMap<String, T>();
+                mData.put(userId, data);
+            }
+            data.put(packageName, obj);
+        }
+
+        @Nullable
+        public T get(int userId, @NonNull String packageName) {
+            ArrayMap<String, T> data = mData.get(userId);
+            if (data != null) {
+                return data.get(packageName);
+            }
+            return null;
+        }
+
+        /** Returns the userId at the given index. */
+        public int keyAt(int index) {
+            return mData.keyAt(index);
+        }
+
+        /** Returns the package name at the given index. */
+        @NonNull
+        public String keyAt(int userIndex, int packageIndex) {
+            return mData.valueAt(userIndex).keyAt(packageIndex);
+        }
+
+        /** Returns the size of the outer (userId) array. */
+        public int numUsers() {
+            return mData.size();
+        }
+
+        public int numPackagesForUser(int userId) {
+            ArrayMap<String, T> data = mData.get(userId);
+            return data == null ? 0 : data.size();
+        }
+
+        /** Returns the value T at the given user and index. */
+        @Nullable
+        public T valueAt(int userIndex, int packageIndex) {
+            return mData.valueAt(userIndex).valueAt(packageIndex);
+        }
+
+        public void forEach(Consumer<T> consumer) {
+            for (int i = numUsers() - 1; i >= 0; --i) {
+                ArrayMap<String, T> data = mData.valueAt(i);
+                for (int j = data.size() - 1; j >= 0; --j) {
+                    consumer.accept(data.valueAt(j));
+                }
+            }
+        }
+    }
+
+    /**
+     * Standardize the output of userId-packageName combo.
+     */
+    private static String string(int userId, String packageName) {
+        return "<" + userId + ">" + packageName;
+    }
+
+    @VisibleForTesting
+    static final class Package {
+        public final String packageName;
+        public final int userId;
+
+        Package(int userId, String packageName) {
+            this.userId = userId;
+            this.packageName = packageName;
+        }
+
+        @Override
+        public String toString() {
+            return string(userId, packageName);
+        }
+
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(StateControllerProto.QuotaController.Package.USER_ID, userId);
+            proto.write(StateControllerProto.QuotaController.Package.NAME, packageName);
+
+            proto.end(token);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Package) {
+                Package other = (Package) obj;
+                return userId == other.userId && Objects.equals(packageName, other.packageName);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return packageName.hashCode() + userId;
+        }
+    }
+
+    /** List of all tracked jobs keyed by source package-userId combo. */
+    private final UserPackageMap<ArraySet<JobStatus>> mTrackedJobs = new UserPackageMap<>();
+
+    /** Timer for each package-userId combo. */
+    private final UserPackageMap<Timer> mPkgTimers = new UserPackageMap<>();
+
+    /** List of all timing sessions for a package-userId combo, in chronological order. */
+    private final UserPackageMap<List<TimingSession>> mTimingSessions = new UserPackageMap<>();
+
+    /**
+     * List of alarm listeners for each package that listen for when each package comes back within
+     * quota.
+     */
+    private final UserPackageMap<QcAlarmListener> mInQuotaAlarmListeners = new UserPackageMap<>();
+
+    private final AlarmManager mAlarmManager;
+    private final ChargingTracker mChargeTracker;
+    private final Handler mHandler;
+
+    private volatile boolean mInParole;
+
+    /**
+     * If the QuotaController should throttle apps based on their standby bucket and job activity.
+     * If false, all jobs will have their CONSTRAINT_WITHIN_QUOTA bit set to true immediately and
+     * indefinitely.
+     */
+    private boolean mShouldThrottle;
+
+    /** How much time each app will have to run jobs within their standby bucket window. */
+    private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
+
+    /**
+     * How much time the package should have before transitioning from out-of-quota to in-quota.
+     * This should not affect processing if the package is already in-quota.
+     */
+    private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
+
+    private long mNextCleanupTimeElapsed = 0;
+    private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
+            new AlarmManager.OnAlarmListener() {
+                @Override
+                public void onAlarm() {
+                    mHandler.obtainMessage(MSG_CLEAN_UP_SESSIONS).sendToTarget();
+                }
+            };
+
+    /**
+     * The rolling window size for each standby bucket. Within each window, an app will have 10
+     * minutes to run its jobs.
+     */
+    private final long[] mBucketPeriodsMs = new long[] {
+            10 * MINUTE_IN_MILLIS, // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
+            2 * 60 * MINUTE_IN_MILLIS, // 2 hours for WORKING
+            8 * 60 * MINUTE_IN_MILLIS, // 8 hours for FREQUENT
+            24 * 60 * MINUTE_IN_MILLIS // 24 hours for RARE
+    };
+
+    /** The maximum period any bucket can have. */
+    private static final long MAX_PERIOD_MS = 24 * 60 * MINUTE_IN_MILLIS;
+
+    /** A package has reached its quota. The message should contain a {@link Package} object. */
+    private static final int MSG_REACHED_QUOTA = 0;
+    /** Drop any old timing sessions. */
+    private static final int MSG_CLEAN_UP_SESSIONS = 1;
+    /** Check if a package is now within its quota. */
+    private static final int MSG_CHECK_PACKAGE = 2;
+
+    public QuotaController(JobSchedulerService service) {
+        super(service);
+        mHandler = new QcHandler(mContext.getMainLooper());
+        mChargeTracker = new ChargingTracker();
+        mChargeTracker.startTracking();
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+
+        // Set up the app standby bucketing tracker
+        UsageStatsManagerInternal usageStats = LocalServices.getService(
+                UsageStatsManagerInternal.class);
+        usageStats.addAppIdleStateChangeListener(new StandbyTracker());
+
+        onConstantsUpdatedLocked();
+    }
+
+    @Override
+    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+        // Still need to track jobs even if mShouldThrottle is false in case it's set to true at
+        // some point.
+        ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
+                jobStatus.getSourcePackageName());
+        if (jobs == null) {
+            jobs = new ArraySet<>();
+            mTrackedJobs.add(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), jobs);
+        }
+        jobs.add(jobStatus);
+        jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
+        jobStatus.setQuotaConstraintSatisfied(!mShouldThrottle || isWithinQuotaLocked(jobStatus));
+    }
+
+    @Override
+    public void prepareForExecutionLocked(JobStatus jobStatus) {
+        if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+        final int userId = jobStatus.getSourceUserId();
+        final String packageName = jobStatus.getSourcePackageName();
+        Timer timer = mPkgTimers.get(userId, packageName);
+        if (timer == null) {
+            timer = new Timer(userId, packageName);
+            mPkgTimers.add(userId, packageName, timer);
+        }
+        timer.startTrackingJob(jobStatus);
+    }
+
+    @Override
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+            boolean forUpdate) {
+        if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
+            Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(),
+                    jobStatus.getSourcePackageName());
+            if (timer != null) {
+                timer.stopTrackingJob(jobStatus);
+            }
+            ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
+                    jobStatus.getSourcePackageName());
+            if (jobs != null) {
+                jobs.remove(jobStatus);
+            }
+        }
+    }
+
+    @Override
+    public void onConstantsUpdatedLocked() {
+        boolean changed = false;
+        if (mShouldThrottle == mConstants.USE_HEARTBEATS) {
+            mShouldThrottle = !mConstants.USE_HEARTBEATS;
+            changed = true;
+        }
+        long newAllowedTimeMs = Math.min(MAX_PERIOD_MS,
+                Math.max(MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS));
+        if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
+            mAllowedTimePerPeriodMs = newAllowedTimeMs;
+            changed = true;
+        }
+        long newQuotaBufferMs = Math.max(0,
+                Math.min(5 * MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS));
+        if (mQuotaBufferMs != newQuotaBufferMs) {
+            mQuotaBufferMs = newQuotaBufferMs;
+            changed = true;
+        }
+        long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS));
+        if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
+            mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
+            changed = true;
+        }
+        long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS));
+        if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
+            mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
+            changed = true;
+        }
+        long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS));
+        if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
+            mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
+            changed = true;
+        }
+        long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS));
+        if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
+            mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
+            changed = true;
+        }
+
+        if (changed) {
+            // Update job bookkeeping out of band.
+            BackgroundThread.getHandler().post(() -> {
+                synchronized (mLock) {
+                    maybeUpdateAllConstraintsLocked();
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns an appropriate standby bucket for the job, taking into account any standby
+     * exemptions.
+     */
+    private int getEffectiveStandbyBucket(@NonNull final JobStatus jobStatus) {
+        if (jobStatus.uidActive || jobStatus.getJob().isExemptedFromAppStandby()) {
+            // Treat these cases as if they're in the ACTIVE bucket so that they get throttled
+            // like other ACTIVE apps.
+            return ACTIVE_INDEX;
+        }
+        return jobStatus.getStandbyBucket();
+    }
+
+    private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
+        final int standbyBucket = getEffectiveStandbyBucket(jobStatus);
+        return isWithinQuotaLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+                standbyBucket);
+    }
+
+    private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+            final int standbyBucket) {
+        if (standbyBucket == NEVER_INDEX) return false;
+        if (standbyBucket == ACTIVE_INDEX) return true;
+        // This check is needed in case the flag is toggled after a job has been registered.
+        if (!mShouldThrottle) return true;
+
+        // Quota constraint is not enforced while charging or when parole is on.
+        return mChargeTracker.isCharging() || mInParole
+                || getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0;
+    }
+
+    @VisibleForTesting
+    long getRemainingExecutionTimeLocked(@NonNull final JobStatus jobStatus) {
+        return getRemainingExecutionTimeLocked(jobStatus.getSourceUserId(),
+                jobStatus.getSourcePackageName(),
+                getEffectiveStandbyBucket(jobStatus));
+    }
+
+    @VisibleForTesting
+    long getRemainingExecutionTimeLocked(final int userId, @NonNull final String packageName) {
+        final int standbyBucket = JobSchedulerService.standbyBucketForPackage(packageName,
+                userId, sElapsedRealtimeClock.millis());
+        return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket);
+    }
+
+    /**
+     * Returns the amount of time, in milliseconds, that this job has remaining to run based on its
+     * current standby bucket. Time remaining could be negative if the app was moved from a less
+     * restricted to a more restricted bucket.
+     */
+    private long getRemainingExecutionTimeLocked(final int userId,
+            @NonNull final String packageName, final int standbyBucket) {
+        if (standbyBucket == NEVER_INDEX) {
+            return 0;
+        }
+        final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+        final long trailingRunDurationMs = getTrailingExecutionTimeLocked(
+                userId, packageName, bucketWindowSizeMs);
+        return mAllowedTimePerPeriodMs - trailingRunDurationMs;
+    }
+
+    /** Returns how long the uid has had jobs running within the most recent window. */
+    @VisibleForTesting
+    long getTrailingExecutionTimeLocked(final int userId, @NonNull final String packageName,
+            final long windowSizeMs) {
+        long totalTime = 0;
+
+        Timer timer = mPkgTimers.get(userId, packageName);
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        if (timer != null && timer.isActive()) {
+            totalTime = timer.getCurrentDuration(nowElapsed);
+        }
+
+        List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+        if (sessions == null || sessions.size() == 0) {
+            return totalTime;
+        }
+
+        final long startElapsed = nowElapsed - windowSizeMs;
+        // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
+        // the most recent ones.
+        for (int i = sessions.size() - 1; i >= 0; --i) {
+            TimingSession session = sessions.get(i);
+            if (startElapsed < session.startTimeElapsed) {
+                totalTime += session.endTimeElapsed - session.startTimeElapsed;
+            } else if (startElapsed < session.endTimeElapsed) {
+                // The session started before the window but ended within the window. Only include
+                // the portion that was within the window.
+                totalTime += session.endTimeElapsed - startElapsed;
+            } else {
+                // This session ended before the window. No point in going any further.
+                return totalTime;
+            }
+        }
+        return totalTime;
+    }
+
+    @VisibleForTesting
+    void saveTimingSession(final int userId, @NonNull final String packageName,
+            @NonNull final TimingSession session) {
+        synchronized (mLock) {
+            List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+            if (sessions == null) {
+                sessions = new ArrayList<>();
+                mTimingSessions.add(userId, packageName, sessions);
+            }
+            sessions.add(session);
+
+            maybeScheduleCleanupAlarmLocked();
+        }
+    }
+
+    private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> {
+        public long earliestEndElapsed = Long.MAX_VALUE;
+
+        @Override
+        public void accept(List<TimingSession> sessions) {
+            if (sessions != null && sessions.size() > 0) {
+                earliestEndElapsed = Math.min(earliestEndElapsed, sessions.get(0).endTimeElapsed);
+            }
+        }
+
+        void reset() {
+            earliestEndElapsed = Long.MAX_VALUE;
+        }
+    }
+
+    private final EarliestEndTimeFunctor mEarliestEndTimeFunctor = new EarliestEndTimeFunctor();
+
+    /** Schedule a cleanup alarm if necessary and there isn't already one scheduled. */
+    @VisibleForTesting
+    void maybeScheduleCleanupAlarmLocked() {
+        if (mNextCleanupTimeElapsed > sElapsedRealtimeClock.millis()) {
+            // There's already an alarm scheduled. Just stick with that one. There's no way we'll
+            // end up scheduling an earlier alarm.
+            if (DEBUG) {
+                Slog.v(TAG, "Not scheduling cleanup since there's already one at "
+                        + mNextCleanupTimeElapsed + " (in " + (mNextCleanupTimeElapsed
+                        - sElapsedRealtimeClock.millis()) + "ms)");
+            }
+            return;
+        }
+        mEarliestEndTimeFunctor.reset();
+        mTimingSessions.forEach(mEarliestEndTimeFunctor);
+        final long earliestEndElapsed = mEarliestEndTimeFunctor.earliestEndElapsed;
+        if (earliestEndElapsed == Long.MAX_VALUE) {
+            // Couldn't find a good time to clean up. Maybe this was called after we deleted all
+            // timing sessions.
+            if (DEBUG) Slog.d(TAG, "Didn't find a time to schedule cleanup");
+            return;
+        }
+        // Need to keep sessions for all apps up to the max period, regardless of their current
+        // standby bucket.
+        long nextCleanupElapsed = earliestEndElapsed + MAX_PERIOD_MS;
+        if (nextCleanupElapsed - mNextCleanupTimeElapsed <= 10 * MINUTE_IN_MILLIS) {
+            // No need to clean up too often. Delay the alarm if the next cleanup would be too soon
+            // after it.
+            nextCleanupElapsed += 10 * MINUTE_IN_MILLIS;
+        }
+        mNextCleanupTimeElapsed = nextCleanupElapsed;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP,
+                mSessionCleanupAlarmListener, mHandler);
+        if (DEBUG) Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed);
+    }
+
+    private void handleNewChargingStateLocked() {
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        final boolean isCharging = mChargeTracker.isCharging();
+        if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
+        // Deal with Timers first.
+        mPkgTimers.forEach((t) -> t.onChargingChanged(nowElapsed, isCharging));
+        // Now update jobs.
+        maybeUpdateAllConstraintsLocked();
+    }
+
+    private void maybeUpdateAllConstraintsLocked() {
+        boolean changed = false;
+        for (int u = 0; u < mTrackedJobs.numUsers(); ++u) {
+            final int userId = mTrackedJobs.keyAt(u);
+            for (int p = 0; p < mTrackedJobs.numPackagesForUser(userId); ++p) {
+                final String packageName = mTrackedJobs.keyAt(u, p);
+                changed |= maybeUpdateConstraintForPkgLocked(userId, packageName);
+            }
+        }
+        if (changed) {
+            mStateChangedListener.onControllerStateChanged();
+        }
+    }
+
+    /**
+     * Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package.
+     *
+     * @return true if at least one job had its bit changed
+     */
+    private boolean maybeUpdateConstraintForPkgLocked(final int userId,
+            @NonNull final String packageName) {
+        ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+        if (jobs == null || jobs.size() == 0) {
+            return false;
+        }
+
+        // Quota is the same for all jobs within a package.
+        final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
+        final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
+        boolean changed = false;
+        for (int i = jobs.size() - 1; i >= 0; --i) {
+            final JobStatus js = jobs.valueAt(i);
+            if (realStandbyBucket == getEffectiveStandbyBucket(js)) {
+                changed |= js.setQuotaConstraintSatisfied(realInQuota);
+            } else {
+                // This job is somehow exempted. Need to determine its own quota status.
+                changed |= js.setQuotaConstraintSatisfied(isWithinQuotaLocked(js));
+            }
+        }
+        if (!realInQuota) {
+            // Don't want to use the effective standby bucket here since that bump the bucket to
+            // ACTIVE for one of the jobs, which doesn't help with other jobs that aren't
+            // exempted.
+            maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket);
+        } else {
+            QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+            if (alarmListener != null) {
+                mAlarmManager.cancel(alarmListener);
+                // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
+                alarmListener.setTriggerTime(0);
+            }
+        }
+        return changed;
+    }
+
+    /**
+     * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run
+     * again. This should only be called if the package is already out of quota.
+     */
+    @VisibleForTesting
+    void maybeScheduleStartAlarmLocked(final int userId, @NonNull final String packageName,
+            final int standbyBucket) {
+        final String pkgString = string(userId, packageName);
+        if (standbyBucket == NEVER_INDEX) {
+            return;
+        } else if (standbyBucket == ACTIVE_INDEX) {
+            // ACTIVE apps are "always" in quota.
+            if (DEBUG) {
+                Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+                        + " even though it is active");
+            }
+            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+
+            QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+            if (alarmListener != null) {
+                // Cancel any pending alarm.
+                mAlarmManager.cancel(alarmListener);
+                // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
+                alarmListener.setTriggerTime(0);
+            }
+            return;
+        }
+
+        List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+        if (sessions == null || sessions.size() == 0) {
+            // If there are no sessions, then the job is probably in quota.
+            if (DEBUG) {
+                Slog.wtf(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+                        + " even though it is likely within its quota.");
+            }
+            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+            return;
+        }
+
+        final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        // How far back we need to look.
+        final long startElapsed = nowElapsed - bucketWindowSizeMs;
+
+        long totalTime = 0;
+        long cutoffTimeElapsed = nowElapsed;
+        for (int i = sessions.size() - 1; i >= 0; i--) {
+            TimingSession session = sessions.get(i);
+            if (startElapsed < session.startTimeElapsed) {
+                cutoffTimeElapsed = session.startTimeElapsed;
+                totalTime += session.endTimeElapsed - session.startTimeElapsed;
+            } else if (startElapsed < session.endTimeElapsed) {
+                // The session started before the window but ended within the window. Only
+                // include the portion that was within the window.
+                cutoffTimeElapsed = startElapsed;
+                totalTime += session.endTimeElapsed - startElapsed;
+            } else {
+                // This session ended before the window. No point in going any further.
+                break;
+            }
+            if (totalTime >= mAllowedTimePerPeriodMs) {
+                break;
+            }
+        }
+        if (totalTime < mAllowedTimePerPeriodMs) {
+            // Already in quota. Why was this method called?
+            if (DEBUG) {
+                Slog.w(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
+                        + " even though it already has " + (mAllowedTimePerPeriodMs - totalTime)
+                        + "ms in its quota.");
+            }
+            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
+            return;
+        }
+
+        QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+        if (alarmListener == null) {
+            alarmListener = new QcAlarmListener(userId, packageName);
+            mInQuotaAlarmListeners.add(userId, packageName, alarmListener);
+        }
+
+        // We add all the way back to the beginning of a session (or the window) even when we don't
+        // need to (in order to simplify the for loop above), so there might be some extra we
+        // need to add back.
+        final long extraTimeMs = totalTime - mAllowedTimePerPeriodMs;
+        // The time this app will have quota again.
+        final long inQuotaTimeElapsed =
+                cutoffTimeElapsed + extraTimeMs + mQuotaBufferMs + bucketWindowSizeMs;
+        // Only schedule the alarm if:
+        // 1. There isn't one currently scheduled
+        // 2. The new alarm is significantly earlier than the previous alarm (which could be the
+        // case if the package moves into a higher standby bucket). If it's earlier but not
+        // significantly so, then we essentially delay the job a few extra minutes.
+        // 3. The alarm is after the current alarm by more than the quota buffer.
+        // TODO: this might be overengineering. Simplify if proven safe.
+        if (!alarmListener.isWaiting()
+                || inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS
+                || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed - mQuotaBufferMs) {
+            if (DEBUG) Slog.d(TAG, "Scheduling start alarm for " + pkgString);
+            // If the next time this app will have quota is at least 3 minutes before the
+            // alarm is supposed to go off, reschedule the alarm.
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed,
+                    ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler);
+            alarmListener.setTriggerTime(inQuotaTimeElapsed);
+        }
+    }
+
+    private final class ChargingTracker extends BroadcastReceiver {
+        /**
+         * Track whether we're charging. This has a slightly different definition than that of
+         * BatteryController.
+         */
+        private boolean mCharging;
+
+        ChargingTracker() {
+        }
+
+        public void startTracking() {
+            IntentFilter filter = new IntentFilter();
+
+            // Charging/not charging.
+            filter.addAction(BatteryManager.ACTION_CHARGING);
+            filter.addAction(BatteryManager.ACTION_DISCHARGING);
+            mContext.registerReceiver(this, filter);
+
+            // Initialise tracker state.
+            BatteryManagerInternal batteryManagerInternal =
+                    LocalServices.getService(BatteryManagerInternal.class);
+            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+        }
+
+        public boolean isCharging() {
+            return mCharging;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                final String action = intent.getAction();
+                if (BatteryManager.ACTION_CHARGING.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Received charging intent, fired @ "
+                                + sElapsedRealtimeClock.millis());
+                    }
+                    mCharging = true;
+                    handleNewChargingStateLocked();
+                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Disconnected from power.");
+                    }
+                    mCharging = false;
+                    handleNewChargingStateLocked();
+                }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static final class TimingSession {
+        // Start timestamp in elapsed realtime timebase.
+        public final long startTimeElapsed;
+        // End timestamp in elapsed realtime timebase.
+        public final long endTimeElapsed;
+        // How many jobs ran during this session.
+        public final int jobCount;
+
+        TimingSession(long startElapsed, long endElapsed, int jobCount) {
+            this.startTimeElapsed = startElapsed;
+            this.endTimeElapsed = endElapsed;
+            this.jobCount = jobCount;
+        }
+
+        @Override
+        public String toString() {
+            return "TimingSession{" + startTimeElapsed + "->" + endTimeElapsed + ", " + jobCount
+                    + "}";
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof TimingSession) {
+                TimingSession other = (TimingSession) obj;
+                return startTimeElapsed == other.startTimeElapsed
+                        && endTimeElapsed == other.endTimeElapsed
+                        && jobCount == other.jobCount;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(new long[] {startTimeElapsed, endTimeElapsed, jobCount});
+        }
+
+        public void dump(IndentingPrintWriter pw) {
+            pw.print(startTimeElapsed);
+            pw.print(" -> ");
+            pw.print(endTimeElapsed);
+            pw.print(" (");
+            pw.print(endTimeElapsed - startTimeElapsed);
+            pw.print("), ");
+            pw.print(jobCount);
+            pw.print(" jobs.");
+            pw.println();
+        }
+
+        public void dump(@NonNull ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(StateControllerProto.QuotaController.TimingSession.START_TIME_ELAPSED,
+                    startTimeElapsed);
+            proto.write(StateControllerProto.QuotaController.TimingSession.END_TIME_ELAPSED,
+                    endTimeElapsed);
+            proto.write(StateControllerProto.QuotaController.TimingSession.JOB_COUNT, jobCount);
+
+            proto.end(token);
+        }
+    }
+
+    private final class Timer {
+        private final Package mPkg;
+
+        // List of jobs currently running for this package.
+        private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
+        private long mStartTimeElapsed;
+        private int mJobCount;
+
+        Timer(int userId, String packageName) {
+            mPkg = new Package(userId, packageName);
+        }
+
+        void startTrackingJob(@NonNull JobStatus jobStatus) {
+            if (DEBUG) Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
+            synchronized (mLock) {
+                // Always track jobs, even when charging.
+                mRunningJobs.add(jobStatus);
+                if (!mChargeTracker.isCharging()) {
+                    mJobCount++;
+                    if (mRunningJobs.size() == 1) {
+                        // Started tracking the first job.
+                        mStartTimeElapsed = sElapsedRealtimeClock.millis();
+                        scheduleCutoff();
+                    }
+                }
+            }
+        }
+
+        void stopTrackingJob(@NonNull JobStatus jobStatus) {
+            if (DEBUG) Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString());
+            synchronized (mLock) {
+                if (mRunningJobs.size() == 0) {
+                    // maybeStopTrackingJobLocked can be called when an app cancels a job, so a
+                    // timer may not be running when it's asked to stop tracking a job.
+                    if (DEBUG) {
+                        Slog.d(TAG, "Timer isn't tracking any jobs but still told to stop");
+                    }
+                    return;
+                }
+                mRunningJobs.remove(jobStatus);
+                if (!mChargeTracker.isCharging() && mRunningJobs.size() == 0) {
+                    emitSessionLocked(sElapsedRealtimeClock.millis());
+                    cancelCutoff();
+                }
+            }
+        }
+
+        private void emitSessionLocked(long nowElapsed) {
+            if (mJobCount <= 0) {
+                // Nothing to emit.
+                return;
+            }
+            TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mJobCount);
+            saveTimingSession(mPkg.userId, mPkg.packageName, ts);
+            mJobCount = 0;
+            // Don't reset the tracked jobs list as we need to keep tracking the current number
+            // of jobs.
+            // However, cancel the currently scheduled cutoff since it's not currently useful.
+            cancelCutoff();
+        }
+
+        /**
+         * Returns true if the Timer is actively tracking, as opposed to passively ref counting
+         * during charging.
+         */
+        public boolean isActive() {
+            synchronized (mLock) {
+                return mJobCount > 0;
+            }
+        }
+
+        long getCurrentDuration(long nowElapsed) {
+            synchronized (mLock) {
+                return !isActive() ? 0 : nowElapsed - mStartTimeElapsed;
+            }
+        }
+
+        void onChargingChanged(long nowElapsed, boolean isCharging) {
+            synchronized (mLock) {
+                if (isCharging) {
+                    emitSessionLocked(nowElapsed);
+                } else {
+                    // Start timing from unplug.
+                    if (mRunningJobs.size() > 0) {
+                        mStartTimeElapsed = nowElapsed;
+                        // NOTE: this does have the unfortunate consequence that if the device is
+                        // repeatedly plugged in and unplugged, the job count for a package may be
+                        // artificially high.
+                        mJobCount = mRunningJobs.size();
+                        // Schedule cutoff since we're now actively tracking for quotas again.
+                        scheduleCutoff();
+                    }
+                }
+            }
+        }
+
+        void rescheduleCutoff() {
+            cancelCutoff();
+            scheduleCutoff();
+        }
+
+        private void scheduleCutoff() {
+            // Each package can only be in one standby bucket, so we only need to have one
+            // message per timer. We only need to reschedule when restarting timer or when
+            // standby bucket changes.
+            synchronized (mLock) {
+                if (!isActive()) {
+                    return;
+                }
+                Message msg = mHandler.obtainMessage(MSG_REACHED_QUOTA, mPkg);
+                final long timeRemainingMs = getRemainingExecutionTimeLocked(mPkg.userId,
+                        mPkg.packageName);
+                if (DEBUG) {
+                    Slog.i(TAG, "Job for " + mPkg + " has " + timeRemainingMs + "ms left.");
+                }
+                // If the job was running the entire time, then the system would be up, so it's
+                // fine to use uptime millis for these messages.
+                mHandler.sendMessageDelayed(msg, timeRemainingMs);
+            }
+        }
+
+        private void cancelCutoff() {
+            mHandler.removeMessages(MSG_REACHED_QUOTA, mPkg);
+        }
+
+        public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
+            pw.print("Timer{");
+            pw.print(mPkg);
+            pw.print("} ");
+            if (isActive()) {
+                pw.print("started at ");
+                pw.print(mStartTimeElapsed);
+            } else {
+                pw.print("NOT active");
+            }
+            pw.print(", ");
+            pw.print(mJobCount);
+            pw.print(" running jobs");
+            pw.println();
+            pw.increaseIndent();
+            for (int i = 0; i < mRunningJobs.size(); i++) {
+                JobStatus js = mRunningJobs.valueAt(i);
+                if (predicate.test(js)) {
+                    pw.println(js.toShortString());
+                }
+            }
+
+            pw.decreaseIndent();
+        }
+
+        public void dump(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate) {
+            final long token = proto.start(fieldId);
+
+            mPkg.writeToProto(proto, StateControllerProto.QuotaController.Timer.PKG);
+            proto.write(StateControllerProto.QuotaController.Timer.IS_ACTIVE, isActive());
+            proto.write(StateControllerProto.QuotaController.Timer.START_TIME_ELAPSED,
+                    mStartTimeElapsed);
+            proto.write(StateControllerProto.QuotaController.Timer.JOB_COUNT, mJobCount);
+            for (int i = 0; i < mRunningJobs.size(); i++) {
+                JobStatus js = mRunningJobs.valueAt(i);
+                if (predicate.test(js)) {
+                    js.writeToShortProto(proto,
+                            StateControllerProto.QuotaController.Timer.RUNNING_JOBS);
+                }
+            }
+
+            proto.end(token);
+        }
+    }
+
+    /**
+     * Tracking of app assignments to standby buckets
+     */
+    final class StandbyTracker extends AppIdleStateChangeListener {
+
+        @Override
+        public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
+                boolean idle, int bucket, int reason) {
+            // Update job bookkeeping out of band.
+            BackgroundThread.getHandler().post(() -> {
+                final int bucketIndex = JobSchedulerService.standbyBucketToBucketIndex(bucket);
+                if (DEBUG) {
+                    Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex "
+                            + bucketIndex);
+                }
+                synchronized (mLock) {
+                    ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+                    if (jobs == null || jobs.size() == 0) {
+                        return;
+                    }
+                    for (int i = jobs.size() - 1; i >= 0; i--) {
+                        JobStatus js = jobs.valueAt(i);
+                        js.setStandbyBucket(bucketIndex);
+                    }
+                    Timer timer = mPkgTimers.get(userId, packageName);
+                    if (timer != null && timer.isActive()) {
+                        timer.rescheduleCutoff();
+                    }
+                    if (!mShouldThrottle || maybeUpdateConstraintForPkgLocked(userId,
+                            packageName)) {
+                        mStateChangedListener.onControllerStateChanged();
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onParoleStateChanged(final boolean isParoleOn) {
+            mInParole = isParoleOn;
+            if (DEBUG) Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
+            // Update job bookkeeping out of band.
+            BackgroundThread.getHandler().post(() -> {
+                synchronized (mLock) {
+                    maybeUpdateAllConstraintsLocked();
+                }
+            });
+        }
+    }
+
+    private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> {
+        private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() {
+            public boolean test(TimingSession ts) {
+                return ts.endTimeElapsed <= sElapsedRealtimeClock.millis() - MAX_PERIOD_MS;
+            }
+        };
+
+        @Override
+        public void accept(List<TimingSession> sessions) {
+            if (sessions != null) {
+                // Remove everything older than MAX_PERIOD_MS time ago.
+                sessions.removeIf(mTooOld);
+            }
+        }
+    }
+
+    private final DeleteTimingSessionsFunctor mDeleteOldSessionsFunctor =
+            new DeleteTimingSessionsFunctor();
+
+    @VisibleForTesting
+    void deleteObsoleteSessionsLocked() {
+        mTimingSessions.forEach(mDeleteOldSessionsFunctor);
+    }
+
+    private class QcHandler extends Handler {
+        QcHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            synchronized (mLock) {
+                switch (msg.what) {
+                    case MSG_REACHED_QUOTA: {
+                        Package pkg = (Package) msg.obj;
+                        if (DEBUG) Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
+
+                        long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
+                                pkg.packageName);
+                        if (timeRemainingMs <= 50) {
+                            // Less than 50 milliseconds left. Start process of shutting down jobs.
+                            if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
+                            if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) {
+                                mStateChangedListener.onControllerStateChanged();
+                            }
+                        } else {
+                            // This could potentially happen if an old session phases out while a
+                            // job is currently running.
+                            // Reschedule message
+                            Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
+                            if (DEBUG) {
+                                Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left.");
+                            }
+                            sendMessageDelayed(rescheduleMsg, timeRemainingMs);
+                        }
+                        break;
+                    }
+                    case MSG_CLEAN_UP_SESSIONS:
+                        if (DEBUG) Slog.d(TAG, "Cleaning up timing sessions.");
+                        deleteObsoleteSessionsLocked();
+                        maybeScheduleCleanupAlarmLocked();
+
+                        break;
+                    case MSG_CHECK_PACKAGE: {
+                        String packageName = (String) msg.obj;
+                        int userId = msg.arg1;
+                        if (DEBUG) Slog.d(TAG, "Checking pkg " + string(userId, packageName));
+                        if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+                            mStateChangedListener.onControllerStateChanged();
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    private class QcAlarmListener implements AlarmManager.OnAlarmListener {
+        private final int mUserId;
+        private final String mPackageName;
+        private volatile long mTriggerTimeElapsed;
+
+        QcAlarmListener(int userId, String packageName) {
+            mUserId = userId;
+            mPackageName = packageName;
+        }
+
+        boolean isWaiting() {
+            return mTriggerTimeElapsed > 0;
+        }
+
+        void setTriggerTime(long timeElapsed) {
+            mTriggerTimeElapsed = timeElapsed;
+        }
+
+        long getTriggerTimeElapsed() {
+            return mTriggerTimeElapsed;
+        }
+
+        @Override
+        public void onAlarm() {
+            mHandler.obtainMessage(MSG_CHECK_PACKAGE, mUserId, 0, mPackageName).sendToTarget();
+            mTriggerTimeElapsed = 0;
+        }
+    }
+
+    //////////////////////// TESTING HELPERS /////////////////////////////
+
+    @VisibleForTesting
+    long getAllowedTimePerPeriodMs() {
+        return mAllowedTimePerPeriodMs;
+    }
+
+    @VisibleForTesting
+    @NonNull
+    long[] getBucketWindowSizes() {
+        return mBucketPeriodsMs;
+    }
+
+    @VisibleForTesting
+    @NonNull
+    Handler getHandler() {
+        return mHandler;
+    }
+
+    @VisibleForTesting
+    long getInQuotaBufferMs() {
+        return mQuotaBufferMs;
+    }
+
+    @VisibleForTesting
+    @Nullable
+    List<TimingSession> getTimingSessions(int userId, String packageName) {
+        return mTimingSessions.get(userId, packageName);
+    }
+
+    //////////////////////////// DATA DUMP //////////////////////////////
+
+    @Override
+    public void dumpControllerStateLocked(final IndentingPrintWriter pw,
+            final Predicate<JobStatus> predicate) {
+        pw.println("Is throttling: " + mShouldThrottle);
+        pw.println("Is charging: " + mChargeTracker.isCharging());
+        pw.println("In parole: " + mInParole);
+        pw.println();
+
+        mTrackedJobs.forEach((jobs) -> {
+            for (int j = 0; j < jobs.size(); j++) {
+                final JobStatus js = jobs.valueAt(j);
+                if (!predicate.test(js)) {
+                    continue;
+                }
+                pw.print("#");
+                js.printUniqueId(pw);
+                pw.print(" from ");
+                UserHandle.formatUid(pw, js.getSourceUid());
+                pw.println();
+
+                pw.increaseIndent();
+                pw.print(JobStatus.bucketName(getEffectiveStandbyBucket(js)));
+                pw.print(", ");
+                if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+                    pw.print("within quota");
+                } else {
+                    pw.print("not within quota");
+                }
+                pw.print(", ");
+                pw.print(getRemainingExecutionTimeLocked(js));
+                pw.print("ms remaining in quota");
+                pw.decreaseIndent();
+                pw.println();
+            }
+        });
+
+        pw.println();
+        for (int u = 0; u < mPkgTimers.numUsers(); ++u) {
+            final int userId = mPkgTimers.keyAt(u);
+            for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) {
+                final String pkgName = mPkgTimers.keyAt(u, p);
+                mPkgTimers.valueAt(u, p).dump(pw, predicate);
+                pw.println();
+                List<TimingSession> sessions = mTimingSessions.get(userId, pkgName);
+                if (sessions != null) {
+                    pw.increaseIndent();
+                    pw.println("Saved sessions:");
+                    pw.increaseIndent();
+                    for (int j = sessions.size() - 1; j >= 0; j--) {
+                        TimingSession session = sessions.get(j);
+                        session.dump(pw);
+                    }
+                    pw.decreaseIndent();
+                    pw.decreaseIndent();
+                    pw.println();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+            Predicate<JobStatus> predicate) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.QUOTA);
+
+        proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging());
+        proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole);
+
+        mTrackedJobs.forEach((jobs) -> {
+            for (int j = 0; j < jobs.size(); j++) {
+                final JobStatus js = jobs.valueAt(j);
+                if (!predicate.test(js)) {
+                    continue;
+                }
+                final long jsToken = proto.start(
+                        StateControllerProto.QuotaController.TRACKED_JOBS);
+                js.writeToShortProto(proto,
+                        StateControllerProto.QuotaController.TrackedJob.INFO);
+                proto.write(StateControllerProto.QuotaController.TrackedJob.SOURCE_UID,
+                        js.getSourceUid());
+                proto.write(
+                        StateControllerProto.QuotaController.TrackedJob.EFFECTIVE_STANDBY_BUCKET,
+                        getEffectiveStandbyBucket(js));
+                proto.write(StateControllerProto.QuotaController.TrackedJob.HAS_QUOTA,
+                        js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+                proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS,
+                        getRemainingExecutionTimeLocked(js));
+                proto.end(jsToken);
+            }
+        });
+
+        for (int u = 0; u < mPkgTimers.numUsers(); ++u) {
+            final int userId = mPkgTimers.keyAt(u);
+            for (int p = 0; p < mPkgTimers.numPackagesForUser(userId); ++p) {
+                final String pkgName = mPkgTimers.keyAt(u, p);
+                final long psToken = proto.start(
+                        StateControllerProto.QuotaController.PACKAGE_STATS);
+                mPkgTimers.valueAt(u, p).dump(proto,
+                        StateControllerProto.QuotaController.PackageStats.TIMER, predicate);
+
+                List<TimingSession> sessions = mTimingSessions.get(userId, pkgName);
+                if (sessions != null) {
+                    for (int j = sessions.size() - 1; j >= 0; j--) {
+                        TimingSession session = sessions.get(j);
+                        session.dump(proto,
+                                StateControllerProto.QuotaController.PackageStats.SAVED_SESSIONS);
+                    }
+                }
+
+                proto.end(psToken);
+            }
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index c2be283..b439c0d 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -53,22 +53,31 @@
      * preexisting tasks.
      */
     public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob);
+
     /**
      * Optionally implement logic here to prepare the job to be executed.
      */
     public void prepareForExecutionLocked(JobStatus jobStatus) {
     }
+
     /**
      * Remove task - this will happen if the task is cancelled, completed, etc.
      */
     public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
             boolean forUpdate);
+
     /**
      * Called when a new job is being created to reschedule an old failed job.
      */
     public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
     }
 
+    /**
+     * Called when the JobScheduler.Constants are updated.
+     */
+    public void onConstantsUpdatedLocked() {
+    }
+
     public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate);
     public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 2584187..9b9f4de 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -15,12 +15,15 @@
  */
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.provider.Settings;
 import android.telecom.TelecomManager;
 
 import com.android.internal.util.NotificationMessagingUtil;
@@ -47,6 +50,21 @@
 
     @Override
     public int compare(NotificationRecord left, NotificationRecord right) {
+        final int leftImportance = left.getImportance();
+        final int rightImportance = right.getImportance();
+        final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
+        final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
+
+        // With new interruption model, prefer importance bucket above all other criteria
+        // (to ensure buckets are contiguous)
+        if (Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
+            if (isLeftHighImportance != isRightHighImportance) {
+                // by importance bucket, high importance higher than low importance
+                return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
+            }
+        }
+
         // first all colorized notifications
         boolean leftImportantColorized = isImportantColorized(left);
         boolean rightImportantColorized = isImportantColorized(right);
@@ -86,8 +104,6 @@
             return -1 * Boolean.compare(leftPeople, rightPeople);
         }
 
-        final int leftImportance = left.getImportance();
-        final int rightImportance = right.getImportance();
         if (leftImportance != rightImportance) {
             // by importance, high to low
             return -1 * Integer.compare(leftImportance, rightImportance);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6c2549e..95e1962 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -207,7 +207,6 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.DeviceIdleController;
 import com.android.server.EventLogTags;
-import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.lights.Light;
@@ -266,7 +265,7 @@
 
     // message codes
     static final int MESSAGE_DURATION_REACHED = 2;
-    // 3: removed to a different handler
+    static final int MESSAGE_SAVE_POLICY_FILE = 3;
     static final int MESSAGE_SEND_RANKING_UPDATE = 4;
     static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
     static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
@@ -428,6 +427,7 @@
     private GroupHelper mGroupHelper;
     private int mAutoGroupAtCount;
     private boolean mIsTelevision;
+    private boolean mIsAutomotive;
 
     private MetricsLogger mMetricsLogger;
     private Predicate<String> mAllowedManagedServicePackages;
@@ -573,7 +573,7 @@
             mListeners.migrateToXml();
             mAssistants.migrateToXml();
             mConditionProviders.migrateToXml();
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         mAssistants.ensureAssistant();
@@ -603,28 +603,34 @@
         }
     }
 
-    private void handleSavePolicyFile() {
-        IoThread.getHandler().post(() -> {
-            if (DBG) Slog.d(TAG, "handleSavePolicyFile");
-            synchronized (mPolicyFile) {
-                final FileOutputStream stream;
-                try {
-                    stream = mPolicyFile.startWrite();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to save policy file", e);
-                    return;
-                }
+    /**
+     * Saves notification policy
+     */
+    public void savePolicyFile() {
+        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
+        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
+    }
 
-                try {
-                    writePolicyXml(stream, false /*forBackup*/);
-                    mPolicyFile.finishWrite(stream);
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to save policy file, restoring backup", e);
-                    mPolicyFile.failWrite(stream);
-                }
+    private void handleSavePolicyFile() {
+        if (DBG) Slog.d(TAG, "handleSavePolicyFile");
+        synchronized (mPolicyFile) {
+            final FileOutputStream stream;
+            try {
+                stream = mPolicyFile.startWrite();
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to save policy file", e);
+                return;
             }
-            BackupManager.dataChanged(getContext().getPackageName());
-        });
+
+            try {
+                writePolicyXml(stream, false /*forBackup*/);
+                mPolicyFile.finishWrite(stream);
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+                mPolicyFile.failWrite(stream);
+            }
+        }
+        BackupManager.dataChanged(getContext().getPackageName());
     }
 
     private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
@@ -1132,8 +1138,8 @@
 
                     }
                 }
-
                 mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
+                savePolicyFile();
             }
         }
     };
@@ -1200,7 +1206,7 @@
                 mListeners.onUserRemoved(userId);
                 mConditionProviders.onUserRemoved(userId);
                 mAssistants.onUserRemoved(userId);
-                handleSavePolicyFile();
+                savePolicyFile();
             } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                 mUserProfiles.updateCache(context);
@@ -1388,6 +1394,11 @@
     }
 
     @VisibleForTesting
+    void setIsAutomotive(boolean isAutomotive) {
+        mIsAutomotive = isAutomotive;
+    }
+
+    @VisibleForTesting
     void setIsTelevision(boolean isTelevision) {
         mIsTelevision = isTelevision;
     }
@@ -1451,7 +1462,7 @@
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
             public void onConfigChanged() {
-                handleSavePolicyFile();
+                savePolicyFile();
             }
 
             @Override
@@ -1543,6 +1554,9 @@
 
         mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
                 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
+
+        mIsAutomotive =
+                mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
     }
 
     @Override
@@ -1750,7 +1764,7 @@
                     modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
         }
 
-        handleSavePolicyFile();
+        savePolicyFile();
     }
 
     private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
@@ -2218,7 +2232,7 @@
                 Slog.w(TAG, "Can't notify app about app block change", e);
             }
 
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         /**
@@ -2275,7 +2289,7 @@
         public void setShowBadge(String pkg, int uid, boolean showBadge) {
             checkCallerIsSystem();
             mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         @Override
@@ -2291,7 +2305,7 @@
                 if (info != null) {
                     mPreferencesHelper.setNotificationDelegate(
                             callingPkg, callingUid, delegate, info.uid);
-                    handleSavePolicyFile();
+                    savePolicyFile();
                 }
             } catch (RemoteException e) {
                 // :(
@@ -2302,7 +2316,7 @@
         public void revokeNotificationDelegate(String callingPkg) {
             checkCallerIsSameApp(callingPkg);
             mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         @Override
@@ -2337,7 +2351,7 @@
                 NotificationChannelGroup group) throws RemoteException {
             enforceSystemOrSystemUI("Caller not system or systemui");
             createNotificationChannelGroup(pkg, uid, group, false, false);
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         @Override
@@ -2350,7 +2364,7 @@
                 final NotificationChannelGroup group = groups.get(i);
                 createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
             }
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         private void createNotificationChannelsImpl(String pkg, int uid,
@@ -2368,7 +2382,7 @@
                         mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
                         NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
             }
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         @Override
@@ -2413,7 +2427,7 @@
                     UserHandle.getUserHandleForUid(callingUid),
                     mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
                     NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         @Override
@@ -2455,7 +2469,7 @@
                 mListeners.notifyNotificationChannelGroupChanged(
                         pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
                         NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
-                handleSavePolicyFile();
+                savePolicyFile();
             }
         }
 
@@ -2588,7 +2602,7 @@
                         true, UserHandle.getCallingUserId(), packages, uids);
             }
 
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
 
@@ -3376,7 +3390,7 @@
                 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
                 try {
                     readPolicyXml(bais, true /*forRestore*/);
-                    handleSavePolicyFile();
+                    savePolicyFile();
                 } catch (NumberFormatException | XmlPullParserException | IOException e) {
                     Slog.w(TAG, "applyRestore: error reading payload", e);
                 }
@@ -3417,7 +3431,7 @@
                                     .setPackage(pkg)
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
-                    handleSavePolicyFile();
+                    savePolicyFile();
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3561,7 +3575,7 @@
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
 
-                    handleSavePolicyFile();
+                    savePolicyFile();
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3587,7 +3601,7 @@
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
 
-                    handleSavePolicyFile();
+                    savePolicyFile();
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3670,7 +3684,7 @@
             verifyPrivilegedListener(token, user, false);
             createNotificationChannelGroup(
                     pkg, getUidForPackageAndUser(pkg, user), group, false, true);
-            handleSavePolicyFile();
+            savePolicyFile();
         }
 
         @Override
@@ -3719,7 +3733,7 @@
             }
             if (allow != mLockScreenAllowSecureNotifications) {
                 mLockScreenAllowSecureNotifications = allow;
-                handleSavePolicyFile();
+                savePolicyFile();
             }
         }
 
@@ -4478,7 +4492,7 @@
                 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
             }
             mSnoozeHelper.update(userId, r);
-            handleSavePolicyFile();
+            savePolicyFile();
             return false;
         }
 
@@ -4609,7 +4623,7 @@
                 mSnoozeHelper.snooze(r, mDuration);
             }
             r.recordSnoozed();
-            handleSavePolicyFile();
+            savePolicyFile();
         }
     }
 
@@ -4687,7 +4701,7 @@
                     if (mReason != REASON_SNOOZED) {
                         final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId);
                         if (wasSnoozed) {
-                            handleSavePolicyFile();
+                            savePolicyFile();
                         }
                     }
                 }
@@ -5131,7 +5145,9 @@
 
         // Should this notification make noise, vibe, or use the LED?
         final boolean aboveThreshold =
-                record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
+                mIsAutomotive
+                        ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
+                        : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
 
         // Remember if this notification already owns the notification channels.
         boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
@@ -5729,6 +5745,9 @@
                 case MESSAGE_FINISH_TOKEN_TIMEOUT:
                     handleKillTokenTimeout((ToastRecord) msg.obj);
                     break;
+                case MESSAGE_SAVE_POLICY_FILE:
+                    handleSavePolicyFile();
+                    break;
                 case MESSAGE_SEND_RANKING_UPDATE:
                     handleSendRankingUpdate();
                     break;
@@ -6246,7 +6265,7 @@
             Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
         }
         mSnoozeHelper.repost(key);
-        handleSavePolicyFile();
+        savePolicyFile();
     }
 
     @GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8abb500..dab4e79 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -58,6 +58,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -10299,12 +10300,21 @@
                         compareSignatures(
                             signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
                             pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
-                        // Treat mismatched signatures on system packages using a shared UID as
-                        // fatal for the system overall, rather than just failing to install
-                        // whichever package happened to be scanned later.
-                        throw new IllegalStateException(
-                                "Signature mismatch on system package " + pkg.packageName
-                                + " for shared user " + pkgSetting.sharedUser);
+                        if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+                            // Mismatched signatures is an error and silently skipping system
+                            // packages will likely break the device in unforeseen ways. However,
+                            // we allow the device to boot anyway because, prior to P, vendors were
+                            // not expecting the platform to crash in this situation.
+                            throw new PackageManagerException(
+                                    INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                    "Signature mismatch for shared user: " + pkgSetting.sharedUser);
+                        } else {
+                            // Treat mismatched signatures on system packages using a shared UID as
+                            // fatal for the system overall, rather than just failing to install
+                            // whichever package happened to be scanned later.
+                            throw new IllegalStateException("Signature mismatch on system package "
+                                + pkg.packageName + " for shared user " + pkgSetting.sharedUser);
+                        }
                     }
 
                     signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
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 68fe1d8..51619cf 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -46,7 +46,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager;
@@ -1059,6 +1058,17 @@
             return;
         }
 
+        // Intersect the requestedPermissions for a factory image with that of its current update
+        // in case the latter one removed a <uses-permission>
+        String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;
+        int size = requestedPermissions.length;
+        for (int i = 0; i < size; i++) {
+            if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {
+                requestedPermissions[i] = null;
+            }
+        }
+        requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);
+
         PackageManager pm = mContext.getPackageManager();
         final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
         ApplicationInfo applicationInfo = pkg.applicationInfo;
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 79e2688..07bebad 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -29,8 +29,12 @@
 import android.os.IThermalService;
 import android.os.IThermalStatusListener;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
 import android.os.Temperature;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -77,6 +81,10 @@
     @GuardedBy("mLock")
     private int mStatus;
 
+    /** If override status takes effect*/
+    @GuardedBy("mLock")
+    private boolean mIsStatusOverride;
+
     /** Current thermal map, key as name */
     @GuardedBy("mLock")
     private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
@@ -184,13 +192,19 @@
                 newStatus = t.getStatus();
             }
         }
+        // Do not update if override from shell
+        if (!mIsStatusOverride) {
+            setStatusLocked(newStatus);
+        }
+    }
+
+    private void setStatusLocked(int newStatus) {
         if (newStatus != mStatus) {
             mStatus = newStatus;
             notifyStatusListenersLocked();
         }
     }
 
-
     private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
             @Nullable Integer type) {
         synchronized (mLock) {
@@ -241,12 +255,7 @@
             // Thermal Shutdown for Skin temperature
             if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
                     && temperature.getType() == Temperature.TYPE_SKIN) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+                mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
             }
 
             Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
@@ -263,8 +272,14 @@
         }
     }
 
+    /* HwBinder callback **/
     private void onTemperatureChangedCallback(Temperature temperature) {
-        onTemperatureChanged(temperature, true);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            onTemperatureChanged(temperature, true);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
@@ -393,7 +408,7 @@
         }
 
         @Override
-        public int getCurrentStatus() {
+        public int getCurrentThermalStatus() {
             synchronized (mLock) {
                 final long token = Binder.clearCallingIdentity();
                 try {
@@ -434,8 +449,93 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        private boolean isCallerShell() {
+            final int callingUid = Binder.getCallingUid();
+            return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+        }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out,
+                FileDescriptor err, String[] args, ShellCallback callback,
+                ResultReceiver resultReceiver) {
+            if (!isCallerShell()) {
+                Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands");
+                return;
+            }
+            (new ThermalShellCommand()).exec(
+                    this, in, out, err, args, callback, resultReceiver);
+        }
+
     };
 
+    class ThermalShellCommand extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            switch(cmd != null ? cmd : "") {
+                case "override-status":
+                    return runOverrideStatus();
+                case "reset":
+                    return runReset();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        }
+
+        private int runReset() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mIsStatusOverride = false;
+                    onTemperatureMapChangedLocked();
+                    return 0;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private int runOverrideStatus() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final PrintWriter pw = getOutPrintWriter();
+                int status;
+                try {
+                    status = Integer.parseInt(getNextArgRequired());
+                } catch (RuntimeException ex) {
+                    pw.println("Error: " + ex.toString());
+                    return -1;
+                }
+                if (!Temperature.isValidStatus(status)) {
+                    pw.println("Invalid status: " + Integer.toString(status));
+                    return -1;
+                }
+                synchronized (mLock) {
+                    mIsStatusOverride = true;
+                    setStatusLocked(status);
+                }
+                return 0;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onHelp() {
+            final PrintWriter pw = getOutPrintWriter();
+            pw.println("Thermal service (thermalservice) commands:");
+            pw.println("  help");
+            pw.println("    Print this help text.");
+            pw.println("");
+            pw.println("  override-status STATUS");
+            pw.println("    sets and locks the thermal status of the device to STATUS.");
+            pw.println("    status code is defined in android.os.Temperature.");
+            pw.println("  reset");
+            pw.println("    unlocks the thermal status of the device.");
+            pw.println();
+        }
+    }
+
     abstract static class ThermalHalWrapper {
         protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
 
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index b065470..b5ad235 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -55,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -106,7 +107,7 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         getContext().registerReceiverAsUser(new BroadcastReceiver() {
             @Override
-            public void onReceive(Context context, Intent intent) {
+            public void onReceive(@NonNull Context context, @NonNull Intent intent) {
                 if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) {
                     int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                     onRemoveUser(userId);
@@ -129,10 +130,11 @@
             userState = getUserStateLocked(userId);
         }
         String packagesHash = computeComponentStateHash(userId);
-        boolean needGrant;
+        String lastGrantPackagesHash;
         synchronized (mLock) {
-            needGrant = !packagesHash.equals(userState.getLastGrantPackagesHashLocked());
+            lastGrantPackagesHash = userState.getLastGrantPackagesHashLocked();
         }
+        boolean needGrant = !Objects.equals(packagesHash, lastGrantPackagesHash);
         if (needGrant) {
             // Some vital packages state has changed since last role grant
             // Run grants again
@@ -144,7 +146,6 @@
                         public void onSuccess() {
                             result.complete(null);
                         }
-
                         @Override
                         public void onFailure() {
                             result.completeExceptionally(new RuntimeException());
@@ -163,7 +164,8 @@
         }
     }
 
-    private String computeComponentStateHash(int userId) {
+    @Nullable
+    private String computeComponentStateHash(@UserIdInt int userId) {
         PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
         ByteArrayOutputStream out = new ByteArrayOutputStream();
 
@@ -198,8 +200,7 @@
     private RoleUserState getUserStateLocked(@UserIdInt int userId) {
         RoleUserState userState = mUserStates.get(userId);
         if (userState == null) {
-            userState = new RoleUserState(userId);
-            userState.readSyncLocked();
+            userState = RoleUserState.newInstanceLocked(userId);
             mUserStates.put(userId, userState);
         }
         return userState;
@@ -386,11 +387,11 @@
         }
 
         @Override
-        public void onShellCommand(FileDescriptor in, FileDescriptor out,
-                FileDescriptor err, String[] args, ShellCallback callback,
-                ResultReceiver resultReceiver) {
-            (new RoleManagerShellCommand(this)).exec(
-                    this, in, out, err, args, callback, resultReceiver);
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err, @NonNull String[] args,
+                @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+            new RoleManagerShellCommand(this).exec(this, in, out, err, args, callback,
+                    resultReceiver);
         }
     }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
index e1977ef..336b311 100644
--- a/services/core/java/com/android/server/role/RoleManagerShellCommand.java
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -16,6 +16,8 @@
 
 package com.android.server.role;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.role.IRoleManager;
 import android.app.role.IRoleManagerCallback;
 import android.os.RemoteException;
@@ -27,13 +29,17 @@
 import java.util.concurrent.TimeUnit;
 
 class RoleManagerShellCommand extends ShellCommand {
+
+    @NonNull
     private final IRoleManager mRoleManager;
 
-    RoleManagerShellCommand(IRoleManager roleManager) {
+    RoleManagerShellCommand(@NonNull IRoleManager roleManager) {
         mRoleManager = roleManager;
     }
 
     private class Callback extends IRoleManagerCallback.Stub {
+
+        @NonNull
         private final CompletableFuture<Void> mResult = new CompletableFuture<>();
 
         public int waitForResult() {
@@ -58,7 +64,7 @@
     }
 
     @Override
-    public int onCommand(String cmd) {
+    public int onCommand(@Nullable String cmd) {
         if (cmd == null) {
             return handleDefaultCommands(cmd);
         }
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index f218d3a..3e3e156 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -47,6 +47,7 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Stores the state of roles for a user.
@@ -70,25 +71,41 @@
     private final int mUserId;
 
     @GuardedBy("RoleManagerService.mLock")
-    private int mVersion;
+    private int mVersion = VERSION_UNDEFINED;
 
     @GuardedBy("RoleManagerService.mLock")
-    private String mLastGrantPackagesHash = null;
+    @Nullable
+    private String mLastGrantPackagesHash;
 
     /**
      * Maps role names to its holders' package names. The values should never be null.
      */
     @GuardedBy("RoleManagerService.mLock")
-    @Nullable
-    private ArrayMap<String, ArraySet<String>> mRoles = null;
+    @NonNull
+    private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
 
     @GuardedBy("RoleManagerService.mLock")
     private boolean mDestroyed;
 
+    @NonNull
     private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
 
-    public RoleUserState(@UserIdInt int userId) {
+    private RoleUserState(@UserIdInt int userId) {
         mUserId = userId;
+
+        readSyncLocked();
+    }
+
+    /**
+     * Create a new instance of user state, and read its state from disk if previously persisted.
+     *
+     * @param userId the user id for the new user state
+     *
+     * @return the new user state
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public static RoleUserState newInstanceLocked(@UserIdInt int userId) {
+        return new RoleUserState(userId);
     }
 
     /**
@@ -116,7 +133,9 @@
     }
 
     /**
-     * Get the hash representing the state of packages during the last time initial grants was run
+     * Get the hash representing the state of packages during the last time initial grants was run.
+     *
+     * @return the hash representing the state of packages
      */
     @GuardedBy("RoleManagerService.mLock")
     public String getLastGrantPackagesHashLocked() {
@@ -124,10 +143,16 @@
     }
 
     /**
-     * Set the hash representing the state of packages during the last time initial grants was run
+     * Set the hash representing the state of packages during the last time initial grants was run.
+     *
+     * @param lastGrantPackagesHash the hash representing the state of packages
      */
     @GuardedBy("RoleManagerService.mLock")
-    public void setLastGrantPackagesHashLocked(String lastGrantPackagesHash) {
+    public void setLastGrantPackagesHashLocked(@Nullable String lastGrantPackagesHash) {
+        throwIfDestroyedLocked();
+        if (Objects.equals(mLastGrantPackagesHash, lastGrantPackagesHash)) {
+            return;
+        }
         mLastGrantPackagesHash = lastGrantPackagesHash;
         writeAsyncLocked();
     }
@@ -250,9 +275,9 @@
      * Schedule writing the state to file.
      */
     @GuardedBy("RoleManagerService.mLock")
-    void writeAsyncLocked() {
+    private void writeAsyncLocked() {
         throwIfDestroyedLocked();
-        int version = mVersion;
+
         ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
         for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
             String roleName = mRoles.keyAt(i);
@@ -260,15 +285,16 @@
             roleHolders = new ArraySet<>(roleHolders);
             roles.put(roleName, roleHolders);
         }
+
         mWriteHandler.removeCallbacksAndMessages(null);
         // TODO: Throttle writes.
-        mWriteHandler.sendMessage(PooledLambda.obtainMessage(
-                RoleUserState::writeSync, this, version, roles, mLastGrantPackagesHash));
+        mWriteHandler.sendMessage(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
+                mVersion, mLastGrantPackagesHash, roles));
     }
 
     @WorkerThread
-    private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles,
-            String packagesHash) {
+    private void writeSync(int version, @Nullable String packagesHash,
+            @NonNull ArrayMap<String, ArraySet<String>> roles) {
         AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
         FileOutputStream out = null;
         try {
@@ -280,7 +306,7 @@
                     "http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startDocument(null, true);
 
-            serializeRoles(serializer, version, roles, packagesHash);
+            serializeRoles(serializer, version, packagesHash, roles);
 
             serializer.endDocument();
             atomicFile.finishWrite(out);
@@ -296,19 +322,26 @@
 
     @WorkerThread
     private void serializeRoles(@NonNull XmlSerializer serializer, int version,
-            @NonNull ArrayMap<String, ArraySet<String>> roles, String packagesHash)
+            @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
             throws IOException {
         serializer.startTag(null, TAG_ROLES);
+
         serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
-        serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
+
+        if (packagesHash != null) {
+            serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
+        }
+
         for (int i = 0, size = roles.size(); i < size; ++i) {
             String roleName = roles.keyAt(i);
             ArraySet<String> roleHolders = roles.valueAt(i);
+
             serializer.startTag(null, TAG_ROLE);
             serializer.attribute(null, ATTRIBUTE_NAME, roleName);
             serializeRoleHolders(serializer, roleHolders);
             serializer.endTag(null, TAG_ROLE);
         }
+
         serializer.endTag(null, TAG_ROLES);
     }
 
@@ -317,6 +350,7 @@
             @NonNull ArraySet<String> roleHolders) throws IOException {
         for (int i = 0, size = roleHolders.size(); i < size; ++i) {
             String roleHolder = roleHolders.valueAt(i);
+
             serializer.startTag(null, TAG_HOLDER);
             serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
             serializer.endTag(null, TAG_HOLDER);
@@ -327,11 +361,7 @@
      * Read the state from file.
      */
     @GuardedBy("RoleManagerService.mLock")
-    public void readSyncLocked() {
-        if (mRoles != null) {
-            throw new IllegalStateException("This RoleUserState has already read the roles.xml");
-        }
-
+    private void readSyncLocked() {
         File file = getFile(mUserId);
         try (FileInputStream in = new AtomicFile(file).openRead()) {
             XmlPullParser parser = Xml.newPullParser();
@@ -339,8 +369,6 @@
             parseXmlLocked(parser);
         } catch (FileNotFoundException e) {
             Slog.i(LOG_TAG, "roles.xml not found");
-            mRoles = new ArrayMap<>();
-            mVersion = VERSION_UNDEFINED;
         } catch (XmlPullParserException | IOException e) {
             throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
         }
@@ -362,13 +390,14 @@
                 return;
             }
         }
+        Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
     }
 
     private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
             XmlPullParserException {
         mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
         mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
-        mRoles = new ArrayMap<>();
+        mRoles.clear();
 
         int type;
         int depth;
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
new file mode 100644
index 0000000..7ce071f
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -0,0 +1,28 @@
+/*
+ * 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.signedconfig;
+
+import android.content.Context;
+
+class SignedConfigApplicator {
+
+    static void applyConfig(Context context, String config, String signature) {
+        //TODO verify signature
+        //TODO parse & apply config
+    }
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
new file mode 100644
index 0000000..1485686
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -0,0 +1,105 @@
+/*
+ * 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.signedconfig;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+/**
+ * Signed config service. This is not an Android Service, but just owns a broadcast receiver for
+ * receiving package install and update notifications from the package manager.
+ */
+public class SignedConfigService {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "SignedConfig";
+
+    // TODO should these be elsewhere? In a public API?
+    private static final String KEY_CONFIG = "android.signedconfig";
+    private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+
+    private static class UpdateReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            new SignedConfigService(context).handlePackageBroadcast(intent);
+        }
+    }
+
+    private final Context mContext;
+    private final PackageManagerInternal mPacMan;
+
+    public SignedConfigService(Context context) {
+        mContext = context;
+        mPacMan = LocalServices.getService(PackageManagerInternal.class);
+    }
+
+    void handlePackageBroadcast(Intent intent) {
+        if (DBG) Slog.d(TAG, "handlePackageBroadcast " + intent);
+        Uri packageData = intent.getData();
+        String packageName = packageData == null ? null : packageData.getSchemeSpecificPart();
+        if (DBG) Slog.d(TAG, "handlePackageBroadcast package=" + packageName);
+        if (packageName == null) {
+            return;
+        }
+        int userId = mContext.getUser().getIdentifier();
+        PackageInfo pi = mPacMan.getPackageInfo(packageName, PackageManager.GET_META_DATA,
+                android.os.Process.SYSTEM_UID, userId);
+        if (pi == null) {
+            Slog.w(TAG, "Got null PackageInfo for " + packageName + "; user " + userId);
+            return;
+        }
+        Bundle metaData = pi.applicationInfo.metaData;
+        if (metaData == null) {
+            if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
+            return;
+        }
+        if (metaData.containsKey(KEY_CONFIG)
+                && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
+            String config = metaData.getString(KEY_CONFIG);
+            String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+            if (DBG) {
+                Slog.d(TAG, "Got signed config: " + config);
+                Slog.d(TAG, "Got config signature: " + signature);
+            }
+            SignedConfigApplicator.applyConfig(mContext, config, signature);
+        } else {
+            if (DBG) Slog.d(TAG, "Package has no config/signature.");
+        }
+    }
+
+    /**
+     * Register to receive broadcasts from the package manager.
+     */
+    public static void registerUpdateReceiver(Context context) {
+        if (DBG) Slog.d(TAG, "Registering receiver");
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addDataScheme("package");
+        context.registerReceiver(new UpdateReceiver(), filter);
+    }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e645b84..6e4c00e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.statusbar;
 
 import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.ActivityThread;
 import android.app.Notification;
@@ -25,6 +26,8 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -40,7 +43,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
-import android.view.Display;
+import android.util.SparseArray;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBar;
@@ -63,7 +66,7 @@
  * A note on locking:  We rely on the fact that calls onto mBar are oneway or
  * if they are local, that they just enqueue messages to not deadlock.
  */
-public class StatusBarManagerService extends IStatusBarService.Stub {
+public class StatusBarManagerService extends IStatusBarService.Stub implements DisplayListener {
     private static final String TAG = "StatusBarManagerService";
     private static final boolean SPEW = false;
 
@@ -79,24 +82,13 @@
     private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
     private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener;
     private IBinder mSysUiVisToken = new Binder();
-    private int mDisabled1 = 0;
-    private int mDisabled2 = 0;
 
     private final Object mLock = new Object();
     private final DeathRecipient mDeathRecipient = new DeathRecipient();
-    // encompasses lights-out mode and other flags defined on View
-    private int mSystemUiVisibility = 0;
-    private int mFullscreenStackSysUiVisibility;
-    private int mDockedStackSysUiVisibility;
-    private final Rect mFullscreenStackBounds = new Rect();
-    private final Rect mDockedStackBounds = new Rect();
-    private boolean mMenuVisible = false;
-    private int mImeWindowVis = 0;
-    private int mImeBackDisposition;
-    private boolean mShowImeSwitcher;
-    private IBinder mImeToken = null;
     private int mCurrentUserId;
 
+    private SparseArray<UiState> mDisplayUiState = new SparseArray<>();
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
             mBar.asBinder().unlinkToDeath(this,0);
@@ -185,8 +177,29 @@
 
         LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
         LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
+
+        // We always have a default display.
+        final UiState state = new UiState();
+        mDisplayUiState.put(DEFAULT_DISPLAY, state);
+
+        final DisplayManager displayManager =
+                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+        displayManager.registerDisplayListener(this, mHandler);
     }
 
+    @Override
+    public void onDisplayAdded(int displayId) {}
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        synchronized (mLock) {
+            mDisplayUiState.remove(displayId);
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {}
+
     /**
      * Private API used by NotificationManagerService.
      */
@@ -240,22 +253,14 @@
 
         @Override
         public void topAppWindowChanged(int displayId, boolean menuVisible) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
-            StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+            StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible);
         }
 
         @Override
         public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
                 int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
                 String cause) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
-            StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+            StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
                     dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
         }
 
@@ -272,13 +277,9 @@
         @Override
         public void appTransitionFinished(int displayId) {
             enforceStatusBarService();
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.appTransitionFinished();
+                    mBar.appTransitionFinished(displayId);
                 } catch (RemoteException ex) {}
             }
         }
@@ -374,39 +375,27 @@
 
         @Override
         public void setWindowState(int displayId, int window, int state) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.setWindowState(window, state);
+                    mBar.setWindowState(displayId, window, state);
                 } catch (RemoteException ex) {}
             }
         }
 
         @Override
         public void appTransitionPending(int displayId) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.appTransitionPending();
+                    mBar.appTransitionPending(displayId);
                 } catch (RemoteException ex) {}
             }
         }
 
         @Override
         public void appTransitionCancelled(int displayId) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.appTransitionCancelled();
+                    mBar.appTransitionCancelled(displayId);
                 } catch (RemoteException ex) {}
             }
         }
@@ -414,14 +403,10 @@
         @Override
         public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
                 long statusBarAnimationsDuration) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
                     mBar.appTransitionStarting(
-                            statusBarAnimationsStartTime, statusBarAnimationsDuration);
+                            displayId, statusBarAnimationsStartTime, statusBarAnimationsDuration);
                 } catch (RemoteException ex) {}
             }
         }
@@ -449,6 +434,7 @@
             return false;
         }
 
+        // TODO(b/118592525): support it per display if necessary.
         @Override
         public void onProposedRotationChanged(int rotation, boolean isValid) {
             if (mBar != null){
@@ -462,7 +448,9 @@
     private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
         @Override
         public boolean isGlobalActionsDisabled() {
-            return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+            // TODO(b/118592525): support global actions for multi-display.
+            final int disabled2 = mDisplayUiState.get(DEFAULT_DISPLAY).getDisabled2();
+            return (disabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
         }
 
         @Override
@@ -664,20 +652,23 @@
         }
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disable(int what, IBinder token, String pkg) {
         disableForUser(what, token, pkg, mCurrentUserId);
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
 
         synchronized (mLock) {
-            disableLocked(userId, what, token, pkg, 1);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
         }
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     /**
      * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
      * To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -689,6 +680,7 @@
         disable2ForUser(what, token, pkg, mCurrentUserId);
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     /**
      * Disable additional status bar features for a given user. Pass the bitwise-or of the
      * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -700,11 +692,12 @@
         enforceStatusBar();
 
         synchronized (mLock) {
-            disableLocked(userId, what, token, pkg, 2);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
         }
     }
 
-    private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) {
+    private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
+            int whichFlag) {
         // It's important that the the callback and the call to mBar get done
         // in the same order when multiple threads are calling this function
         // so they are paired correctly.  The messages on the handler will be
@@ -723,22 +716,19 @@
                 disabledData += "    ([" + i + "] " + tok + "), ";
             }
             disabledData += " }";
-            Log.d(TAG, "disabledlocked (b/113914868): net1=" + net1 + ", mDisabled1=" + mDisabled1
-                    + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => "
-                    + disabledData);
-        }
+            final UiState state = getUiState(displayId);
 
-        if (net1 != mDisabled1 || net2 != mDisabled2) {
-            mDisabled1 = net1;
-            mDisabled2 = net2;
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        mNotificationDelegate.onSetDisabled(net1);
-                    }
-                });
+            Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+                    + ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+                    + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
+        }
+        final UiState state = getUiState(displayId);
+        if (state.disableEquals(net1, net2)) {
+            state.setDisabled(net1, net2);
+            mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
             if (mBar != null) {
                 try {
-                    mBar.disable(net1, net2);
+                    mBar.disable(displayId, net1, net2);
                 } catch (RemoteException ex) {
                 }
             }
@@ -808,26 +798,27 @@
      * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
      * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
      */
-    private void topAppWindowChanged(final boolean menuVisible) {
+    private void topAppWindowChanged(int displayId, final boolean menuVisible) {
         enforceStatusBar();
 
-        if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
-
+        if (SPEW) {
+            Slog.d(TAG, "display#" + displayId + ": "
+                    + (menuVisible ? "showing" : "hiding") + " MENU key");
+        }
         synchronized(mLock) {
-            mMenuVisible = menuVisible;
-            mHandler.post(new Runnable() {
-                public void run() {
-                    if (mBar != null) {
-                        try {
-                            mBar.topAppWindowChanged(menuVisible);
-                        } catch (RemoteException ex) {
-                        }
+            getUiState(displayId).setMenuVisible(menuVisible);
+            mHandler.post(() -> {
+                if (mBar != null) {
+                    try {
+                        mBar.topAppWindowChanged(displayId, menuVisible);
+                    } catch (RemoteException ex) {
                     }
                 }
             });
         }
     }
 
+    // TODO(b/117478341): support back button change when IME is showing on a external display.
     @Override
     public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
             final boolean showImeSwitcher) {
@@ -841,39 +832,42 @@
             // In case of IME change, we need to call up setImeWindowStatus() regardless of
             // mImeWindowVis because mImeWindowVis may not have been set to false when the
             // previous IME was destroyed.
-            mImeWindowVis = vis;
-            mImeBackDisposition = backDisposition;
-            mImeToken = token;
-            mShowImeSwitcher = showImeSwitcher;
-            mHandler.post(new Runnable() {
-                public void run() {
-                    if (mBar != null) {
-                        try {
-                            mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
-                        } catch (RemoteException ex) {
-                        }
-                    }
-                }
+            // TODO(b/117478341): support back button change when IME is showing on a external
+            // display.
+            getUiState(DEFAULT_DISPLAY)
+                    .setImeWindowState(vis, backDisposition, showImeSwitcher, token);
+
+            mHandler.post(() -> {
+                if (mBar == null) return;
+                try {
+                    // TODO(b/117478341): support back button change when IME is showing on a
+                    // external display.
+                    mBar.setImeWindowStatus(
+                            DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher);
+                } catch (RemoteException ex) { }
             });
         }
     }
 
     @Override
-    public void setSystemUiVisibility(int vis, int mask, String cause) {
-        setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+    public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) {
+        final UiState state = getUiState(displayId);
+        setSystemUiVisibility(displayId, vis, 0, 0, mask,
+                state.mFullscreenStackBounds, state.mDockedStackBounds, cause);
     }
 
-    private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
-            Rect fullscreenBounds, Rect dockedBounds, String cause) {
+    private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) {
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
         if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
 
         synchronized (mLock) {
-            updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+            updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask,
                     fullscreenBounds, dockedBounds);
             disableLocked(
+                    displayId,
                     mCurrentUserId,
                     vis & StatusBarManager.DISABLE_MASK,
                     mSysUiVisToken,
@@ -881,30 +875,107 @@
         }
     }
 
-    private void updateUiVisibilityLocked(final int vis,
+    private void updateUiVisibilityLocked(final int displayId, final int vis,
             final int fullscreenStackVis, final int dockedStackVis, final int mask,
             final Rect fullscreenBounds, final Rect dockedBounds) {
-        if (mSystemUiVisibility != vis
-                || mFullscreenStackSysUiVisibility != fullscreenStackVis
-                || mDockedStackSysUiVisibility != dockedStackVis
-                || !mFullscreenStackBounds.equals(fullscreenBounds)
-                || !mDockedStackBounds.equals(dockedBounds)) {
+        final UiState state = getUiState(displayId);
+        if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis,
+                fullscreenBounds, dockedBounds)) {
+            state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds,
+                    dockedBounds);
+            mHandler.post(() -> {
+                if (mBar != null) {
+                    try {
+                        mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
+                                dockedStackVis, mask, fullscreenBounds, dockedBounds);
+                    } catch (RemoteException ex) {
+                        Log.w(TAG, "Can not get StatusBar!");
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * @return {@link UiState} specified by {@code displayId}.
+     *
+     * <p>
+     *   Note: If {@link UiState} specified by {@code displayId} does not exist, {@link UiState}
+     *   will be allocated and {@code mDisplayUiState} will be updated accordingly.
+     * <p/>
+     */
+    private UiState getUiState(int displayId) {
+        UiState state = mDisplayUiState.get(displayId);
+        if (state == null) {
+            state = new UiState();
+            mDisplayUiState.put(displayId, state);
+        }
+        return state;
+    }
+
+    private class UiState {
+        private int mSystemUiVisibility = 0;
+        private int mFullscreenStackSysUiVisibility = 0;
+        private int mDockedStackSysUiVisibility = 0;
+        private final Rect mFullscreenStackBounds = new Rect();
+        private final Rect mDockedStackBounds = new Rect();
+        private boolean mMenuVisible = false;
+        private int mDisabled1 = 0;
+        private int mDisabled2 = 0;
+        private int mImeWindowVis = 0;
+        private int mImeBackDisposition = 0;
+        private boolean mShowImeSwitcher = false;
+        private IBinder mImeToken = null;
+
+        private int getDisabled1() {
+            return mDisabled1;
+        }
+
+        private int getDisabled2() {
+            return mDisabled2;
+        }
+
+        private void setDisabled(int disabled1, int disabled2) {
+            mDisabled1 = disabled1;
+            mDisabled2 = disabled2;
+        }
+
+        private boolean isMenuVisible() {
+            return mMenuVisible;
+        }
+
+        private void setMenuVisible(boolean menuVisible) {
+            mMenuVisible = menuVisible;
+        }
+
+        private boolean disableEquals(int disabled1, int disabled2) {
+            return mDisabled1 == disabled1 && mDisabled2 == disabled2;
+        }
+
+        private void setSystemUiState(final int vis, final int fullscreenStackVis,
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
             mSystemUiVisibility = vis;
             mFullscreenStackSysUiVisibility = fullscreenStackVis;
             mDockedStackSysUiVisibility = dockedStackVis;
             mFullscreenStackBounds.set(fullscreenBounds);
             mDockedStackBounds.set(dockedBounds);
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        if (mBar != null) {
-                            try {
-                                mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
-                                        mask, fullscreenBounds, dockedBounds);
-                            } catch (RemoteException ex) {
-                            }
-                        }
-                    }
-                });
+        }
+
+        private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis,
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+            return mSystemUiVisibility == vis
+                && mFullscreenStackSysUiVisibility == fullscreenStackVis
+                && mDockedStackSysUiVisibility == dockedStackVis
+                && mFullscreenStackBounds.equals(fullscreenBounds)
+                && mDockedStackBounds.equals(dockedBounds);
+        }
+
+        private void setImeWindowState(final int vis, final int backDisposition,
+                final boolean showImeSwitcher, final IBinder token) {
+            mImeWindowVis = vis;
+            mImeBackDisposition = backDisposition;
+            mShowImeSwitcher = showImeSwitcher;
+            mImeToken = token;
         }
     }
 
@@ -939,6 +1010,7 @@
     // ================================================================================
     // Callbacks from the status bar service.
     // ================================================================================
+    // TODO(b/118592525): refactor it as an IStatusBar API.
     @Override
     public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
             List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
@@ -956,18 +1028,21 @@
             }
         }
         synchronized (mLock) {
+            // TODO(b/118592525): Currently, status bar only works on the default display.
+            // Make it aware of multi-display if needed.
+            final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
             switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
-            switches[1] = mSystemUiVisibility;
-            switches[2] = mMenuVisible ? 1 : 0;
-            switches[3] = mImeWindowVis;
-            switches[4] = mImeBackDisposition;
-            switches[5] = mShowImeSwitcher ? 1 : 0;
+            switches[1] = state.mSystemUiVisibility;
+            switches[2] = state.mMenuVisible ? 1 : 0;
+            switches[3] = state.mImeWindowVis;
+            switches[4] = state.mImeBackDisposition;
+            switches[5] = state.mShowImeSwitcher ? 1 : 0;
             switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
-            switches[7] = mFullscreenStackSysUiVisibility;
-            switches[8] = mDockedStackSysUiVisibility;
-            binders.add(mImeToken);
-            fullscreenStackBounds.set(mFullscreenStackBounds);
-            dockedStackBounds.set(mDockedStackBounds);
+            switches[7] = state.mFullscreenStackSysUiVisibility;
+            switches[8] = state.mDockedStackSysUiVisibility;
+            binders.add(state.mImeToken);
+            fullscreenStackBounds.set(state.mFullscreenStackBounds);
+            dockedStackBounds.set(state.mDockedStackBounds);
         }
     }
 
@@ -1309,8 +1384,13 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
         synchronized (mLock) {
-            pw.println("  mDisabled1=0x" + Integer.toHexString(mDisabled1));
-            pw.println("  mDisabled2=0x" + Integer.toHexString(mDisabled2));
+            for (int i = 0; i < mDisplayUiState.size(); i++) {
+                final int key = mDisplayUiState.keyAt(i);
+                final UiState state = mDisplayUiState.get(key);
+                pw.println("  displayId=" + key);
+                pw.println("    mDisabled1=0x" + Integer.toHexString(state.getDisabled1()));
+                pw.println("    mDisabled2=0x" + Integer.toHexString(state.getDisabled2()));
+            }
             final int N = mDisableRecords.size();
             pw.println("  mDisableRecords.size=" + N);
             for (int i=0; i<N; i++) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index cfec8ef..64ff9cf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2822,8 +2822,6 @@
         IoUtils.closeQuietly(stream);
 
         if (!success) {
-            wpdData.mWidth = -1;
-            wpdData.mHeight = -1;
             wallpaper.cropHint.set(0, 0, 0, 0);
             wpdData.mPadding.set(0, 0, 0, 0);
             wallpaper.name = "";
@@ -2839,6 +2837,7 @@
             }
         }
 
+        ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
         ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 25399ef..daf4b8b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1640,7 +1640,6 @@
                 final IAppTransitionAnimationSpecsFuture specsFuture =
                         pendingOptions.getSpecsFuture();
                 if (specsFuture != null) {
-                    // TODO(multidisplay): Shouldn't be really used anymore from next CL.
                     displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
                             specsFuture, pendingOptions.getOnAnimationStartListener(),
                             animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
@@ -1669,7 +1668,6 @@
                         .overridePendingAppTransitionStartCrossProfileApps();
                 break;
             case ANIM_REMOTE_ANIMATION:
-                // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
                 displayContent.mAppTransition.overridePendingAppTransitionRemote(
                         pendingOptions.getRemoteAnimationAdapter());
                 break;
@@ -2543,7 +2541,7 @@
 
         // Bounds changed...update configuration to match.
         if (!matchParentBounds()) {
-            task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
+            task.computeOverrideConfiguration(mTmpConfig, updatedBounds,
                     false /* overrideWidth */, false /* overrideHeight */);
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d40a4fa..3ccede0 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -4944,9 +4944,12 @@
                 }
             }
 
-            mTmpBounds.put(task.taskId, task.getOverrideBounds());
-            if (tempTaskInsetBounds != null) {
-                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+            if (task.hasDisplayedBounds()) {
+                mTmpBounds.put(task.taskId, task.getDisplayedBounds());
+                mTmpInsetBounds.put(task.taskId, task.getOverrideBounds());
+            } else {
+                mTmpBounds.put(task.taskId, task.getOverrideBounds());
+                mTmpInsetBounds.put(task.taskId, null);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0cdbedb..0fc890a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -402,7 +402,8 @@
             int wakefulness);
 
     /** Writes the current window process states to the proto stream. */
-    public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage);
+    public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage,
+            int wakeFullness, boolean testPssMode);
 
     /** Dump the current activities state. */
     public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0967afd..61eb9d4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -863,6 +863,13 @@
      */
     Configuration getGlobalConfigurationForCallingPid() {
         final int pid = Binder.getCallingPid();
+        return getGlobalConfigurationForPid(pid);
+    }
+
+    /**
+     * Return the global configuration used by the process corresponding to the given pid.
+     */
+    Configuration getGlobalConfigurationForPid(int pid) {
         if (pid == MY_PID || pid < 0) {
             return getGlobalConfiguration();
         }
@@ -4701,26 +4708,21 @@
         }
     }
 
-    void writeSleepStateToProto(ProtoOutputStream proto) {
+    private void writeSleepStateToProto(ProtoOutputStream proto, int wakeFullness,
+            boolean testPssMode) {
+        final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
+        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
+                PowerManagerInternal.wakefulnessToProtoEnum(wakeFullness));
         for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) {
             proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
                     st.toString());
         }
-
-        if (mRunningVoice != null) {
-            final long vrToken = proto.start(
-                    ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
-            proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
-                    mRunningVoice.toString());
-            mVoiceWakeLock.writeToProto(
-                    proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
-            proto.end(vrToken);
-        }
-
         proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
         proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN,
                 mShuttingDown);
-        mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+        proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE,
+                testPssMode);
+        proto.end(sleepToken);
     }
 
     int getCurrentUserId() {
@@ -6599,12 +6601,24 @@
         }
 
         @Override
-        public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) {
+        public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage,
+                int wakeFullness, boolean testPssMode) {
             synchronized (mGlobalLock) {
                 if (dumpPackage == null) {
                     getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION);
                     proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
-                    writeSleepStateToProto(proto);
+                    writeSleepStateToProto(proto, wakeFullness, testPssMode);
+                    if (mRunningVoice != null) {
+                        final long vrToken = proto.start(
+                                ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+                        proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
+                                mRunningVoice.toString());
+                        mVoiceWakeLock.writeToProto(
+                                proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
+                        proto.end(vrToken);
+                    }
+                    mVrController.writeToProto(proto,
+                            ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
                     if (mController != null) {
                         final long token = proto.start(CONTROLLER);
                         proto.write(CONTROLLER, mController.toString());
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fb93d39..c458c94 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2178,9 +2178,9 @@
         final TaskStack stack = getStack();
         final Task task = getTask();
         if (task != null && task.inFreeformWindowingMode()) {
-            task.getRelativePosition(outPosition);
+            task.getRelativeDisplayedPosition(outPosition);
         } else if (stack != null) {
-            stack.getRelativePosition(outPosition);
+            stack.getRelativeDisplayedPosition(outPosition);
         }
 
         // Always use stack bounds in order to have the ability to animate outside the task region.
@@ -2193,6 +2193,18 @@
         outBounds.offsetTo(0, 0);
     }
 
+    @Override
+    Rect getDisplayedBounds() {
+        final Task task = getTask();
+        if (task != null) {
+            final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
+            if (!overrideDisplayedBounds.isEmpty()) {
+                return overrideDisplayedBounds;
+            }
+        }
+        return getBounds();
+    }
+
     boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
             boolean isVoiceInteraction) {
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 64553a8..e779d1b 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -319,6 +319,13 @@
         onOverrideConfigurationChanged(mTmpConfig);
     }
 
+    /** Sets the windowing mode for the configuration container. */
+    void setDisplayWindowingMode(int windowingMode) {
+        mTmpConfig.setTo(getOverrideConfiguration());
+        mTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
+        onOverrideConfigurationChanged(mTmpConfig);
+    }
+
     /**
      * Returns true if this container is currently in multi-window mode. I.e. sharing the screen
      * with another activity.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 05e8267..aba2eb3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1546,6 +1546,7 @@
         final int dh = displayInfo.logicalHeight;
         config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
         config.windowConfiguration.setWindowingMode(getWindowingMode());
+        config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
         config.windowConfiguration.setRotation(displayInfo.rotation);
 
         final float density = mDisplayMetrics.density;
@@ -1953,6 +1954,17 @@
         mWmService.mWindowsChanged = true;
     }
 
+    @Override
+    public void setWindowingMode(int windowingMode) {
+        super.setWindowingMode(windowingMode);
+        super.setDisplayWindowingMode(windowingMode);
+    }
+
+    @Override
+    void setDisplayWindowingMode(int windowingMode) {
+        setWindowingMode(windowingMode);
+    }
+
     /**
      * In split-screen mode we process the IME containers above the docked divider
      * rather than directly above their target.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 581cec9..d4bd91b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -222,6 +222,7 @@
     private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     private volatile boolean mHdmiPlugged;
 
+    private volatile boolean mHasStatusBar;
     private volatile boolean mHasNavigationBar;
     // Can the navigation bar ever move to the side?
     private volatile boolean mNavigationBarCanMove;
@@ -523,6 +524,7 @@
         mNavigationBarCanMove = width != height && shortSizeDp < 600;
 
         if (mDisplayContent.isDefaultDisplay) {
+            mHasStatusBar = true;
             mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
 
             // Allow a system property to override this. Used by the emulator.
@@ -534,6 +536,7 @@
                 mHasNavigationBar = true;
             }
         } else {
+            mHasStatusBar = false;
             mHasNavigationBar = mDisplayContent.getDisplay().supportsSystemDecorations();
         }
     }
@@ -589,6 +592,10 @@
         return mHasNavigationBar;
     }
 
+    public boolean hasStatusBar() {
+        return mHasStatusBar;
+    }
+
     public boolean navigationBarCanMove() {
         return mNavigationBarCanMove;
     }
@@ -2493,12 +2500,19 @@
         final int landscapeRotation = displayRotation.getLandscapeRotation();
         final int seascapeRotation = displayRotation.getSeascapeRotation();
 
-        mStatusBarHeightForRotation[portraitRotation] =
-        mStatusBarHeightForRotation[upsideDownRotation] =
-                res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
-        mStatusBarHeightForRotation[landscapeRotation] =
-        mStatusBarHeightForRotation[seascapeRotation] =
-                res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+        if (hasStatusBar()) {
+            mStatusBarHeightForRotation[portraitRotation] =
+                    mStatusBarHeightForRotation[upsideDownRotation] =
+                            res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
+            mStatusBarHeightForRotation[landscapeRotation] =
+                    mStatusBarHeightForRotation[seascapeRotation] =
+                            res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+        } else {
+            mStatusBarHeightForRotation[portraitRotation] =
+                    mStatusBarHeightForRotation[upsideDownRotation] =
+                            mStatusBarHeightForRotation[landscapeRotation] =
+                                    mStatusBarHeightForRotation[seascapeRotation] = 0;
+        }
 
         // Height of the navigation bar when presented horizontally at bottom
         mNavigationBarHeightForRotationDefault[portraitRotation] =
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 88b22cb..c1e9a73 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -194,7 +194,7 @@
             final boolean hasFocus, final boolean hasWallpaper) {
         // Add a window to our list of input windows.
         inputWindowHandle.name = child.toString();
-        flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);
+        flags = child.getSurfaceTouchableRegion(inputWindowHandle.touchableRegion, flags);
         inputWindowHandle.layoutParamsFlags = flags;
         inputWindowHandle.layoutParamsType = type;
         inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b6609e4..83ba384 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -504,10 +504,12 @@
     public void binderDied() {
         cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
 
-        // Clear associated input consumers on runner death
-        final InputMonitor inputMonitor =
-                mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
-        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        synchronized (mService.getWindowManagerLock()) {
+            // Clear associated input consumers on runner death
+            final InputMonitor inputMonitor =
+                    mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+            inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        }
     }
 
     void checkAnimationReady(WallpaperController wallpaperController) {
@@ -603,8 +605,8 @@
             mTask = task;
             mIsRecentTaskInvisible = isRecentTaskInvisible;
             final WindowContainer container = mTask.getParent();
-            container.getRelativePosition(mPosition);
-            container.getBounds(mBounds);
+            container.getRelativeDisplayedPosition(mPosition);
+            mBounds.set(container.getDisplayedBounds());
         }
 
         RemoteAnimationTarget createRemoteAnimationApp() {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 35264a2..8f18aa5 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -252,7 +252,7 @@
      * before mContainer has been updated, any relevant properties (like {@param windowingMode})
      * need to be passed in.
      */
-    public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
+    public void adjustConfigurationForBounds(Rect bounds,
             Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
             boolean overrideHeight, float density, Configuration config,
             Configuration parentConfig, int windowingMode) {
@@ -303,11 +303,9 @@
                 // Additionally task dimensions should not be bigger than its parents dimensions.
                 // The non decor inset are areas that could never be removed in Honeycomb. See
                 // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
-                intersectDisplayBoundsExcludeInsets(nonDecorBounds,
-                        insetBounds != null ? insetBounds : bounds, mTmpNonDecorInsets,
+                intersectDisplayBoundsExcludeInsets(nonDecorBounds, bounds, mTmpNonDecorInsets,
                         mTmpDisplayBounds, overrideWidth, overrideHeight);
-                intersectDisplayBoundsExcludeInsets(stableBounds,
-                        insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
+                intersectDisplayBoundsExcludeInsets(stableBounds, bounds, mTmpStableInsets,
                         mTmpDisplayBounds, overrideWidth, overrideHeight);
                 width = Math.min((int) (stableBounds.width() / density),
                         parentConfig.screenWidthDp);
@@ -323,7 +321,7 @@
             config.screenWidthDp = width;
             config.screenHeightDp = height;
             config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
-                    insetBounds != null ? insetBounds : bounds, density, windowingMode);
+                    bounds, density, windowingMode);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d3db5df..c70f075 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,11 +27,11 @@
 import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
 import static com.android.server.wm.TaskProto.ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
 import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -66,8 +66,8 @@
     final Rect mPreparedFrozenBounds = new Rect();
     final Configuration mPreparedFrozenMergedConfig = new Configuration();
 
-    // Bounds used to calculate the insets.
-    private final Rect mTempInsetBounds = new Rect();
+    // If non-empty, bounds used to display the task during animations/interactions.
+    private final Rect mOverrideDisplayedBounds = new Rect();
 
     /** ID of the display which rotation {@link #mRotation} has. */
     private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
@@ -307,23 +307,23 @@
     }
 
     /**
-     * Sets the bounds used to calculate the insets. See
-     * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
+     * Sets bounds that override where the task is displayed. Used during transient operations
+     * like animation / interaction.
      */
-    void setTempInsetBounds(Rect tempInsetBounds) {
-        if (tempInsetBounds != null) {
-            mTempInsetBounds.set(tempInsetBounds);
+    void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
+        if (overrideDisplayedBounds != null) {
+            mOverrideDisplayedBounds.set(overrideDisplayedBounds);
         } else {
-            mTempInsetBounds.setEmpty();
+            mOverrideDisplayedBounds.setEmpty();
         }
     }
 
     /**
-     * Gets the bounds used to calculate the insets. See
+     * Gets the bounds that override where the task is displayed. See
      * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
      */
-    void getTempInsetBounds(Rect out) {
-        out.set(mTempInsetBounds);
+    Rect getOverrideDisplayedBounds() {
+        return mOverrideDisplayedBounds;
     }
 
     void setResizeable(int resizeMode) {
@@ -380,8 +380,13 @@
         } else {
             mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
         }
-        setTempInsetBounds(tempInsetBounds);
-        setBounds(mTmpRect2, false /* forced */);
+        if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
+            setOverrideDisplayedBounds(null);
+            setBounds(mTmpRect2);
+        } else {
+            setOverrideDisplayedBounds(mTmpRect2);
+            setBounds(tempInsetBounds);
+        }
     }
 
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
@@ -407,6 +412,15 @@
         mStack.getDisplayContent().getBounds(out);
     }
 
+    @Override
+    public Rect getDisplayedBounds() {
+        if (mOverrideDisplayedBounds.isEmpty()) {
+            return super.getDisplayedBounds();
+        } else {
+            return mOverrideDisplayedBounds;
+        }
+    }
+
     /**
      * Calculate the maximum visible area of this task. If the task has only one app,
      * the result will be visible frame of that app. If the task has more than one apps,
@@ -723,7 +737,7 @@
         }
         proto.write(FILLS_PARENT, matchParentBounds());
         getBounds().writeToProto(proto, BOUNDS);
-        mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+        mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
         proto.write(DEFER_REMOVAL, mDeferRemoval);
         proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
         proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
@@ -739,7 +753,7 @@
         pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
         pw.println(doublePrefix + "mdr=" + mDeferRemoval);
         pw.println(doublePrefix + "appTokens=" + mChildren);
-        pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
+        pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
 
         final String triplePrefix = doublePrefix + "  ";
         final String quadruplePrefix = triplePrefix + "  ";
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 4ae2a79..1fb7979 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -44,6 +44,7 @@
 import android.app.WindowConfiguration;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.Slog;
@@ -526,12 +527,24 @@
         adjustBoundsToAvoidConflict(display, inOutBounds);
     }
 
+    private int convertOrientationToScreenOrientation(int orientation) {
+        switch (orientation) {
+            case Configuration.ORIENTATION_LANDSCAPE:
+                return SCREEN_ORIENTATION_LANDSCAPE;
+            case Configuration.ORIENTATION_PORTRAIT:
+                return SCREEN_ORIENTATION_PORTRAIT;
+            default:
+                return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+    }
+
     private int resolveOrientation(@NonNull ActivityRecord root, @NonNull ActivityDisplay display,
             @NonNull Rect bounds) {
         int orientation = resolveOrientation(root);
 
         if (orientation == SCREEN_ORIENTATION_LOCKED) {
-            orientation = bounds.isEmpty() ? display.getConfiguration().orientation
+            orientation = bounds.isEmpty()
+                    ? convertOrientationToScreenOrientation(display.getConfiguration().orientation)
                     : orientationFromBounds(bounds);
             if (DEBUG) {
                 appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 30751bc..3cf0bd7 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -310,6 +310,11 @@
     // This number will be assigned when we evaluate OOM scores for all visible tasks.
     int mLayerRank = -1;
 
+    // When non-empty, this represents the bounds this task will be drawn at. This gets set during
+    // transient operations such as split-divider dragging and animations.
+    // TODO(b/119687367): This member is temporary.
+    final Rect mDisplayedBounds = new Rect();
+
     /** Helper object used for updating override configuration. */
     private Configuration mTmpConfig = new Configuration();
 
@@ -447,6 +452,9 @@
         }
 
         mWindowContainerController = controller;
+        if (!mDisplayedBounds.isEmpty() && controller.mContainer != null) {
+            controller.mContainer.setOverrideDisplayedBounds(mDisplayedBounds);
+        }
     }
 
     void removeWindowContainer() {
@@ -1732,9 +1740,15 @@
         final Configuration newOverrideConfig = new Configuration();
         if (bounds != null) {
             newOverrideConfig.setTo(getOverrideConfiguration());
-            mTmpRect.set(bounds);
+            if (insetBounds != null && !insetBounds.isEmpty()) {
+                mTmpRect.set(insetBounds);
+                setDisplayedBounds(bounds);
+            } else {
+                mTmpRect.set(bounds);
+                setDisplayedBounds(null);
+            }
             adjustForMinimalTaskDimensions(mTmpRect);
-            computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
+            computeOverrideConfiguration(newOverrideConfig, mTmpRect,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
         }
 
@@ -1782,16 +1796,23 @@
                 setLastNonFullscreenBounds(currentBounds);
             }
             setBounds(null);
+            setDisplayedBounds(null);
             newConfig.unset();
         } else {
-            mTmpRect.set(bounds);
+            if (insetBounds != null && !insetBounds.isEmpty()) {
+                mTmpRect.set(insetBounds);
+                setDisplayedBounds(bounds);
+            } else {
+                mTmpRect.set(bounds);
+                setDisplayedBounds(null);
+            }
             adjustForMinimalTaskDimensions(mTmpRect);
             setBounds(mTmpRect);
 
             if (mStack == null || persistBounds) {
                 setLastNonFullscreenBounds(getOverrideBounds());
             }
-            computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
+            computeOverrideConfiguration(newConfig, mTmpRect,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
         }
         onOverrideConfigurationChanged(newConfig);
@@ -1847,11 +1868,44 @@
         mService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
     }
 
+    /**
+     * Displayed bounds are used to set where the task is drawn at any given time. This is
+     * separate from its actual bounds so that the app doesn't see any meaningful configuration
+     * changes during transitionary periods.
+     */
+    void setDisplayedBounds(Rect bounds) {
+        if (bounds == null) {
+            mDisplayedBounds.setEmpty();
+        } else {
+            mDisplayedBounds.set(bounds);
+        }
+        final TaskWindowContainerController controller = getWindowContainerController();
+        if (controller != null && controller.mContainer != null) {
+            controller.mContainer.setOverrideDisplayedBounds(
+                    mDisplayedBounds.isEmpty() ? null : mDisplayedBounds);
+        }
+    }
+
+    /**
+     * Gets the current overridden displayed bounds. These will be empty if the task is not
+     * currently overriding where it is displayed.
+     */
+    Rect getDisplayedBounds() {
+        return mDisplayedBounds;
+    }
+
+    /**
+     * @return {@code true} if this has overridden displayed bounds.
+     */
+    boolean hasDisplayedBounds() {
+        return !mDisplayedBounds.isEmpty();
+    }
+
     /** Clears passed config and fills it with new override values. */
     // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
     // depend on task or stacks, but uses those object to get the display to base the calculation
     // on. Probably best to centralize calculations like this in ConfigurationContainer.
-    void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
+    void computeOverrideConfiguration(Configuration config, Rect bounds,
             boolean overrideWidth, boolean overrideHeight) {
         mTmpNonDecorBounds.set(bounds);
         mTmpStableBounds.set(bounds);
@@ -1863,7 +1917,7 @@
 
         if (mStack != null) {
             final StackWindowController stackController = mStack.getWindowContainerController();
-            stackController.adjustConfigurationForBounds(bounds, insetBounds,
+            stackController.adjustConfigurationForBounds(bounds,
                     mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
                     config, parentConfig, getWindowingMode());
         } else {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b1f95f8..b16e184c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -184,9 +184,15 @@
         // Update bounds of containing tasks.
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
             final Task task = mChildren.get(taskNdx);
-            task.setBounds(taskBounds.get(task.mTaskId), false /* forced */);
-            task.setTempInsetBounds(taskTempInsetBounds != null ?
-                    taskTempInsetBounds.get(task.mTaskId) : null);
+            final Rect insetBounds =
+                    taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) : null;
+            if (insetBounds != null) {
+                task.setBounds(insetBounds);
+                task.setOverrideDisplayedBounds(taskBounds.get(task.mTaskId));
+            } else {
+                task.setBounds(taskBounds.get(task.mTaskId));
+                task.setOverrideDisplayedBounds(null);
+            }
         }
         return true;
     }
@@ -739,7 +745,7 @@
             return;
         }
 
-        final Rect stackBounds = getBounds();
+        final Rect stackBounds = getDisplayedBounds();
         int width = stackBounds.width();
         int height = stackBounds.height();
 
@@ -1754,14 +1760,6 @@
         scheduleAnimation();
     }
 
-    @Override
-    void getRelativePosition(Point outPos) {
-        super.getRelativePosition(outPos);
-        final int outset = getStackOutset();
-        outPos.x -= outset;
-        outPos.y -= outset;
-    }
-
     AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
         return mAnimatingAppWindowTokenRegistry;
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1275ca0..c30cc17 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1266,7 +1266,7 @@
             return;
         }
 
-        getRelativePosition(mTmpPos);
+        getRelativeDisplayedPosition(mTmpPos);
         if (mTmpPos.equals(mLastSurfacePosition)) {
             return;
         }
@@ -1275,12 +1275,22 @@
         mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
     }
 
-    void getRelativePosition(Point outPos) {
-        final Rect bounds = getBounds();
-        outPos.set(bounds.left, bounds.top);
+    /**
+     * Displayed bounds specify where to display this container at. It differs from bounds during
+     * certain operations (like animation or interactive dragging).
+     *
+     * @return the bounds to display this container at.
+     */
+    Rect getDisplayedBounds() {
+        return getBounds();
+    }
+
+    void getRelativeDisplayedPosition(Point outPos) {
+        final Rect dispBounds = getDisplayedBounds();
+        outPos.set(dispBounds.left, dispBounds.top);
         final WindowContainer parent = getParent();
         if (parent != null) {
-            final Rect parentBounds = parent.getBounds();
+            final Rect parentBounds = parent.getDisplayedBounds();
             outPos.offset(-parentBounds.left, -parentBounds.top);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index e83b863..9f1a587 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ClipData;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
@@ -449,11 +448,4 @@
      * Return the display Id for given window.
      */
     public abstract int getDisplayIdForWindow(IBinder windowToken);
-
-    // TODO: use WindowProcessController once go/wm-unified is done.
-    /**
-     * Notifies the window manager that configuration of the process associated with the input pid
-     * changed.
-     */
-    public abstract void onProcessConfigurationChanged(int pid, Configuration newConfig);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 52b24b3..002d6d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -735,6 +735,7 @@
     final InputManagerService mInputManager;
     final DisplayManagerInternal mDisplayManagerInternal;
     final DisplayManager mDisplayManager;
+    final ActivityTaskManagerService mAtmService;
 
     // Indicates whether this device supports wide color gamut / HDR rendering
     private boolean mHasWideColorGamutSupport;
@@ -897,11 +898,10 @@
 
     public static WindowManagerService main(final Context context, final InputManagerService im,
             final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
-            final WindowManagerGlobalLock globalLock) {
+            ActivityTaskManagerService atm) {
         DisplayThread.getHandler().runWithScissors(() ->
                 sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
-                        globalLock),
-                0);
+                        atm), 0);
         return sInstance;
     }
 
@@ -923,9 +923,10 @@
 
     private WindowManagerService(Context context, InputManagerService inputManager,
             boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
-            WindowManagerGlobalLock globalLock) {
+            ActivityTaskManagerService atm) {
         installLock(this, INDEX_WINDOW);
-        mGlobalLock = globalLock;
+        mGlobalLock = atm.getGlobalLock();
+        mAtmService = atm;
         mContext = context;
         mAllowBootMessages = showBootMsgs;
         mOnlyCore = onlyCore;
@@ -7281,19 +7282,6 @@
                 return Display.INVALID_DISPLAY;
             }
         }
-
-        @Override
-        public void onProcessConfigurationChanged(int pid, Configuration newConfig) {
-            synchronized (mGlobalLock) {
-                Configuration currentConfig = mProcessConfigurations.get(pid);
-                if (currentConfig == null) {
-                    currentConfig = new Configuration(newConfig);
-                } else {
-                    currentConfig.setTo(newConfig);
-                }
-                mProcessConfigurations.put(pid, currentConfig);
-            }
-        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ef22bb8..e115fed 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -790,6 +790,18 @@
     }
 
     @Override
+    public Rect getDisplayedBounds() {
+        final Task task = getTask();
+        if (task != null) {
+            Rect bounds = task.getOverrideDisplayedBounds();
+            if (!bounds.isEmpty()) {
+                return bounds;
+            }
+        }
+        return super.getDisplayedBounds();
+    }
+
+    @Override
     public void computeFrameLw() {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
@@ -805,16 +817,7 @@
         final boolean windowsAreFloating = task != null && task.isFloating();
         final DisplayContent dc = getDisplayContent();
 
-        // If the task has temp inset bounds set, we have to make sure all its windows uses
-        // the temp inset frame. Otherwise different display frames get applied to the main
-        // window and the child window, making them misaligned.
-        // Otherwise we need to clear the inset frame, to avoid using a stale frame after leaving
-        // multi window mode.
-        if (task != null && isInMultiWindowMode()) {
-            task.getTempInsetBounds(mInsetFrame);
-        } else {
-            mInsetFrame.setEmpty();
-        }
+        mInsetFrame.set(getBounds());
 
         // Denotes the actual frame used to calculate the insets and to perform the layout. When
         // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
@@ -834,7 +837,7 @@
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
-            getBounds(mWindowFrames.mContainingFrame);
+            mWindowFrames.mContainingFrame.set(getDisplayedBounds());
             if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
 
                 // If the bounds are frozen, we still want to translate the window freely and only
@@ -884,14 +887,9 @@
 
             layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
             mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
-            layoutXDiff =
-                    !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
-                            : 0;
-            layoutYDiff =
-                    !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
-                            : 0;
-            layoutContainingFrame =
-                    !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
+            layoutXDiff = mInsetFrame.left - mWindowFrames.mContainingFrame.left;
+            layoutYDiff = mInsetFrame.top - mWindowFrames.mContainingFrame.top;
+            layoutContainingFrame = mInsetFrame;
             mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
             subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
                     mTmpRect);
@@ -2145,7 +2143,7 @@
         }
     }
 
-    int getTouchableRegion(Region region, int flags) {
+    int getSurfaceTouchableRegion(Region region, int flags) {
         final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
         if (modal && mAppToken != null) {
             // Limit the outer touch to the activity stack region.
@@ -2173,12 +2171,9 @@
             region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
         } else {
             // Not modal or full screen modal
-            getTouchableRegion(region);
+            getTouchableRegion(region, true /* forSurface */);
         }
 
-        // The area containing the shadows is not touchable.
-        region.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
-
         return flags;
     }
 
@@ -2268,8 +2263,10 @@
         // For child windows we want to use the pid for the parent window in case the the child
         // window was added from another process.
         final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid;
-        mTempConfiguration.setTo(mWmService.mProcessConfigurations.get(
-                pid, mWmService.mRoot.getConfiguration()));
+        final Configuration processConfig =
+                mWmService.mAtmService.getGlobalConfigurationForPid(pid);
+        mTempConfiguration.setTo(processConfig == null
+                ? mWmService.mRoot.getConfiguration() : processConfig);
         return mTempConfiguration;
     }
 
@@ -2810,7 +2807,13 @@
                 frame.right - inset.right, frame.bottom - inset.bottom);
     }
 
+    /** Get the touchable region in global coordinates. */
     void getTouchableRegion(Region outRegion) {
+        getTouchableRegion(outRegion, false /* forSurface */);
+    }
+
+    /** If {@param forSuface} is {@code true}, the region will be translated to surface based. */
+    private void getTouchableRegion(Region outRegion, boolean forSurface) {
         if (inPinnedWindowingMode() && !isFocused()) {
             outRegion.setEmpty();
             return;
@@ -2821,22 +2824,26 @@
             default:
             case TOUCHABLE_INSETS_FRAME:
                 outRegion.set(frame);
-                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_CONTENT:
                 applyInsets(outRegion, frame, mGivenContentInsets);
-                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_VISIBLE:
                 applyInsets(outRegion, frame, mGivenVisibleInsets);
-                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_REGION: {
                 outRegion.set(mGivenTouchableRegion);
                 break;
             }
         }
-        outRegion.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
+
+        if (forSurface) {
+            if (mTouchableInsets != TOUCHABLE_INSETS_REGION) {
+                outRegion.translate(-frame.left, -frame.top);
+            }
+            outRegion.getBounds(mTmpRect);
+            applyInsets(outRegion, mTmpRect, mAttrs.surfaceInsets);
+        }
     }
 
     private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -4614,7 +4621,7 @@
             outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
                     -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
         } else if (parentWindowContainer != null) {
-            final Rect parentBounds = parentWindowContainer.getBounds();
+            final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
             outPoint.offset(-parentBounds.left, -parentBounds.top);
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8bc6db7..fb5c556 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1020,7 +1020,7 @@
                         mTmpPos.x = 0;
                         mTmpPos.y = 0;
                         if (stack != null) {
-                            stack.getRelativePosition(mTmpPos);
+                            stack.getRelativeDisplayedPosition(mTmpPos);
                         }
 
                         xOffset = -mTmpPos.x;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 8b873e3..b85489a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,8 +2,6 @@
     name: "libservices.core",
     defaults: ["libservices.core-libs"],
 
-    cpp_std: "c++17",
-
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index b9dabb9..65d3245 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -120,4 +120,9 @@
             int userHandle) {
         return false;
     }
+
+    @Override
+    public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+        return Collections.emptyList();
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6fbb850..f68f4d7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -13426,10 +13426,25 @@
         enforceCrossUsersPermission(userHandle);
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
-            if (admin != null && admin.mCrossProfileCalendarPackages != null) {
+            if (admin != null) {
                 return admin.mCrossProfileCalendarPackages.contains(packageName);
             }
         }
         return false;
     }
+
+    @Override
+    public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+        if (!mHasFeature) {
+            return Collections.emptyList();
+        }
+        enforceCrossUsersPermission(userHandle);
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+            if (admin != null) {
+                return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+            }
+        }
+        return Collections.emptyList();
+    }
 }
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 108f91c..05b8201 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -16,6 +16,7 @@
 package com.android.server.intelligence;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
@@ -25,12 +26,13 @@
 import android.service.intelligence.SnapshotData;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 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.infra.AbstractRemoteService;
 import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
 
@@ -98,8 +100,10 @@
      * 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);
+            int autofillSessionId, @NonNull AutofillId focusedId,
+            @Nullable AutofillValue focusedValue) {
+        mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId,
+                focusedValue);
         if (mAutofillCallback == null) {
             mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId);
         }
@@ -146,7 +150,7 @@
     }
 
     @Override // from RemoteScreenObservationServiceCallbacks
-    public void onServiceDied(AbstractRemoteService service) {
+    public void onServiceDied(AbstractRemoteService<?> service) {
         // TODO(b/111276913): implement (remove session from PerUserSession?)
         if (mService.isDebug()) {
             Slog.d(TAG, "onServiceDied() for " + mId);
@@ -176,6 +180,10 @@
         pw.println(mAutofillCallback != null);
     }
 
+    String toShortString() {
+        return mId.getValue() + ":" + mActivityToken;
+    }
+
     @Override
     public String toString() {
         return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index e0d47d2..a760cbd 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.intelligence;
 
+import static android.Manifest.permission.MANAGE_SMART_SUGGESTIONS;
 import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
 
 import android.annotation.NonNull;
@@ -26,9 +27,15 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.intelligence.InteractionSessionId;
+import android.util.Slog;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IIntelligenceManager;
@@ -37,11 +44,12 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
-import com.android.server.AbstractMasterSystemService;
 import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -56,6 +64,10 @@
 
     private static final String TAG = "IntelligenceManagerService";
 
+    static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
+
+    private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
     @GuardedBy("mLock")
     private ActivityManagerInternal mAm;
 
@@ -66,12 +78,6 @@
     }
 
     @Override // from AbstractMasterSystemService
-    protected String getServiceSettingsProperty() {
-        // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
-        return "smart_suggestions_service";
-    }
-
-    @Override // from AbstractMasterSystemService
     protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
         return new IntelligencePerUserService(this, mLock, resolvedUserId);
@@ -90,6 +96,66 @@
         service.destroyLocked();
     }
 
+    @Override // from AbstractMasterSystemService
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_SMART_SUGGESTIONS, TAG);
+    }
+
+    @Override // from AbstractMasterSystemService
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMP_SERVICE_DURATION_MS;
+    }
+
+    // Called by Shell command.
+    void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+        Slog.i(TAG, "destroySessions() for userId " + userId);
+        enforceCallingPermissionForManagement();
+
+        synchronized (mLock) {
+            if (userId != UserHandle.USER_ALL) {
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.destroySessionsLocked();
+                }
+            } else {
+                visitServicesLocked((s) -> s.destroySessionsLocked());
+            }
+        }
+
+        try {
+            receiver.send(0, new Bundle());
+        } catch (RemoteException e) {
+            // Just ignore it...
+        }
+    }
+
+    // Called by Shell command.
+    void listSessions(int userId, IResultReceiver receiver) {
+        Slog.i(TAG, "listSessions() for userId " + userId);
+        enforceCallingPermissionForManagement();
+
+        final Bundle resultData = new Bundle();
+        final ArrayList<String> sessions = new ArrayList<>();
+
+        synchronized (mLock) {
+            if (userId != UserHandle.USER_ALL) {
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.listSessionsLocked(sessions);
+                }
+            } else {
+                visitServicesLocked((s) -> s.listSessionsLocked(sessions));
+            }
+        }
+
+        resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
+        try {
+            receiver.send(0, resultData);
+        } catch (RemoteException e) {
+            // Just ignore it...
+        }
+    }
+
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -119,7 +185,7 @@
             synchronized (mLock) {
                 final IntelligencePerUserService service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, componentName, taskId, displayId,
-                        sessionId, flags, result);
+                        sessionId, flags, mAllowInstantService, result);
             }
         }
 
@@ -154,6 +220,14 @@
                 dumpLocked("", pw);
             }
         }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+                throws RemoteException {
+            new IntelligenceServiceShellCommand(IntelligenceManagerService.this).exec(
+                    this, in, out, err, args, callback, resultReceiver);
+        }
     }
 
     private final class LocalService extends IntelligenceManagerInternal {
@@ -184,12 +258,13 @@
         @Override
         public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
                 @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
-                int autofillSessionId, @NonNull AutofillId focusedId) {
+                int autofillSessionId, @NonNull AutofillId focusedId,
+                @Nullable AutofillValue focusedValue) {
             synchronized (mLock) {
                 final IntelligencePerUserService service = peekServiceForUserLocked(userId);
                 if (service != null) {
                     return service.requestAutofill(client, activityToken, autofillSessionId,
-                            focusedId);
+                            focusedId, focusedValue);
                 }
             }
             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 e3b09c6..c8448e1 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -36,19 +36,22 @@
 import android.os.RemoteException;
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.SnapshotData;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.ContentCaptureManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
-import com.android.server.AbstractPerUserSystemService;
+import com.android.server.infra.AbstractPerUserSystemService;
 import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -76,17 +79,24 @@
     protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
             throws NameNotFoundException {
 
+        int flags = PackageManager.GET_META_DATA;
+        final boolean isTemp = isTemporaryServiceSetLocked();
+        if (!isTemp) {
+            flags |= PackageManager.MATCH_SYSTEM_ONLY;
+        }
+
         ServiceInfo si;
         try {
-            // TODO(b/111276913): must check that either the service is from a system component,
-            // or it matches a service set by shell cmd (so it can be used on CTS tests and when
-            // OEMs are implementing the real service
-            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
-                    PackageManager.GET_META_DATA, mUserId);
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId);
         } catch (RemoteException e) {
             Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
             return null;
         }
+        if (si == null) {
+            Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)")
+                    + " " + serviceComponent.flattenToShortString());
+            return null;
+        }
         if (!Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE.equals(si.permission)) {
             Slog.w(TAG, "SmartSuggestionsService from '" + si.packageName
                     + "' does not require permission "
@@ -104,11 +114,18 @@
         return super.updateLocked(disabled);
     }
 
+    @Override // from PerUserSystemService
+    protected String getDefaultComponentName() {
+        final String name = getContext()
+                .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService);
+        return TextUtils.isEmpty(name) ? null : name;
+    }
+
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
             @NonNull ComponentName componentName, int taskId, int displayId,
-            @NonNull InteractionSessionId sessionId, int flags,
+            @NonNull InteractionSessionId sessionId, int flags, boolean bindInstantServiceAllowed,
             @NonNull IResultReceiver resultReceiver) {
         if (!isEnabledLocked()) {
             sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
@@ -138,9 +155,6 @@
             return;
         }
 
-        // TODO(b/117779333): get from mMaster once it's moved to superclass
-        final boolean bindInstantServiceAllowed = false;
-
         session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
                 this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
                 bindInstantServiceAllowed, mMaster.verbose);
@@ -253,6 +267,11 @@
     @GuardedBy("mLock")
     public void destroyLocked() {
         if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
+        destroySessionsLocked();
+    }
+
+    @GuardedBy("mLock")
+    void destroySessionsLocked() {
         final int numSessions = mSessions.size();
         for (int i = 0; i < numSessions; i++) {
             final ContentCaptureSession session = mSessions.valueAt(i);
@@ -261,14 +280,25 @@
         mSessions.clear();
     }
 
+    @GuardedBy("mLock")
+    void listSessionsLocked(ArrayList<String> output) {
+        final int numSessions = mSessions.size();
+        for (int i = 0; i < numSessions; i++) {
+            final ContentCaptureSession session = mSessions.valueAt(i);
+            output.add(session.toShortString());
+        }
+    }
+
     public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
-            @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
+            @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId,
+            @Nullable AutofillValue focusedValue) {
         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);
+                return session.requestAutofillLocked(client, autofillSessionId, focusedId,
+                        focusedValue);
             }
             if (mMaster.debug) {
                 Slog.d(TAG, "requestAutofill(): no session for " + activityToken);
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
new file mode 100644
index 0000000..0d92a97
--- /dev/null
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
@@ -0,0 +1,229 @@
+/*
+ * 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.intelligence;
+
+import static com.android.server.intelligence.IntelligenceManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Shell Command implementation for {@link IntelligenceManagerService}.
+ */
+//TODO(b/111276913): rename once the final name is defined
+public final class IntelligenceServiceShellCommand extends ShellCommand {
+
+    private final IntelligenceManagerService mService;
+
+    public IntelligenceServiceShellCommand(@NonNull IntelligenceManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "list":
+                return requestList(pw);
+            case "destroy":
+                return requestDestroy(pw);
+            case "get":
+                return requestGet(pw);
+            case "set":
+                return requestSet(pw);
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter();) {
+            // TODO(b/111276913): rename "intelligence" once SELinux rule changed
+            pw.println("Intelligence Service (intelligence) commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  get bind-instant-service-allowed");
+            pw.println("    Gets whether binding to services provided by instant apps is allowed");
+            pw.println("");
+            pw.println("  set bind-instant-service-allowed [true | false]");
+            pw.println("    Sets whether binding to services provided by instant apps is allowed");
+            pw.println("");
+            pw.println("  set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+            pw.println("    Temporarily (for DURATION ms) changes the service implemtation.");
+            pw.println("    To reset, call with just the USER_ID argument.");
+            pw.println("");
+            pw.println("  list sessions [--user USER_ID]");
+            pw.println("    Lists all pending sessions.");
+            pw.println("");
+            pw.println("  destroy sessions [--user USER_ID]");
+            pw.println("    Destroys all pending sessions.");
+            pw.println("");
+        }
+    }
+
+    private int requestGet(PrintWriter pw) {
+        final String what = getNextArgRequired();
+        switch(what) {
+            case "bind-instant-service-allowed":
+                return getBindInstantService(pw);
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
+        }
+    }
+
+    private int requestSet(PrintWriter pw) {
+        final String what = getNextArgRequired();
+
+        switch(what) {
+            case "bind-instant-service-allowed":
+                return setBindInstantService(pw);
+            case "temporary-service":
+                return setTemporaryService();
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
+        }
+    }
+
+    private int getBindInstantService(PrintWriter pw) {
+        if (mService.getAllowInstantService()) {
+            pw.println("true");
+        } else {
+            pw.println("false");
+        }
+        return 0;
+    }
+
+    private int setBindInstantService(PrintWriter pw) {
+        final String mode = getNextArgRequired();
+        switch (mode.toLowerCase()) {
+            case "true":
+                mService.setAllowInstantService(true);
+                return 0;
+            case "false":
+                mService.setAllowInstantService(false);
+                return 0;
+            default:
+                pw.println("Invalid mode: " + mode);
+                return -1;
+        }
+    }
+
+    private int setTemporaryService() {
+        final int userId = getNextIntArgRequired();
+        final String serviceName = getNextArg();
+        if (serviceName == null) {
+            mService.resetTemporaryService(userId);
+            return 0;
+        }
+        final int duration = getNextIntArgRequired();
+        mService.setTemporaryService(userId, serviceName, duration);
+        return 0;
+    }
+
+    private int requestDestroy(PrintWriter pw) {
+        if (!isNextArgSessions(pw)) {
+            return -1;
+        }
+
+        final int userId = getUserIdFromArgsOrAllUsers();
+        final CountDownLatch latch = new CountDownLatch(1);
+        final IResultReceiver receiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) {
+                latch.countDown();
+            }
+        };
+        return requestSessionCommon(pw, latch, () -> mService.destroySessions(userId, receiver));
+    }
+
+    private int requestList(PrintWriter pw) {
+        if (!isNextArgSessions(pw)) {
+            return -1;
+        }
+
+        final int userId = getUserIdFromArgsOrAllUsers();
+        final CountDownLatch latch = new CountDownLatch(1);
+        final IResultReceiver receiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) {
+                final ArrayList<String> sessions = resultData
+                        .getStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS);
+                for (String session : sessions) {
+                    pw.println(session);
+                }
+                latch.countDown();
+            }
+        };
+        return requestSessionCommon(pw, latch, () -> mService.listSessions(userId, receiver));
+    }
+
+    private boolean isNextArgSessions(PrintWriter pw) {
+        final String type = getNextArgRequired();
+        if (!type.equals("sessions")) {
+            pw.println("Error: invalid list type");
+            return false;
+        }
+        return true;
+    }
+
+    private int requestSessionCommon(PrintWriter pw, CountDownLatch latch,
+            Runnable command) {
+        command.run();
+        return waitForLatch(pw, latch);
+    }
+
+    private int waitForLatch(PrintWriter pw, CountDownLatch latch) {
+        try {
+            final boolean received = latch.await(5, TimeUnit.SECONDS);
+            if (!received) {
+                pw.println("Timed out after 5 seconds");
+                return -1;
+            }
+        } catch (InterruptedException e) {
+            pw.println("System call interrupted");
+            Thread.currentThread().interrupt();
+            return -1;
+        }
+        return 0;
+    }
+
+    private int getUserIdFromArgsOrAllUsers() {
+        if ("--user".equals(getNextArg())) {
+            return UserHandle.parseUserArg(getNextArgRequired());
+        }
+        return UserHandle.USER_ALL;
+    }
+
+    private int getNextIntArgRequired() {
+        return Integer.parseInt(getNextArgRequired());
+    }
+}
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index d9f4f20..c4fbdca 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -23,6 +23,7 @@
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.intelligence.ContentCaptureEventsRequest;
 import android.service.intelligence.IIntelligenceService;
 import android.service.intelligence.InteractionContext;
@@ -32,11 +33,12 @@
 import android.util.Slog;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
 import com.android.internal.os.IResultReceiver;
-import com.android.server.AbstractMultiplePendingRequestsRemoteService;
+import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
 
 import java.util.List;
 
@@ -114,10 +116,10 @@
      */
     public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId,
             @NonNull IAutoFillManagerClient client, int autofillSessionId,
-            @NonNull AutofillId focusedId) {
+            @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue) {
         cancelScheduledUnbind();
         scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId,
-                focusedId));
+                focusedId, focusedValue));
     }
 
     /**
@@ -222,16 +224,20 @@
 
     private static final class PendingAutofillRequest extends MyPendingRequest {
         private final @NonNull AutofillId mFocusedId;
+        private final @Nullable AutofillValue mFocusedValue;
         private final @NonNull IAutoFillManagerClient mClient;
         private final int mAutofillSessionId;
+        private final long mRequestTime = SystemClock.elapsedRealtime();
 
         protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service,
                 @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client,
-                int autofillSessionId, @NonNull AutofillId focusedId) {
+                int autofillSessionId, @NonNull AutofillId focusedId,
+                @Nullable AutofillValue focusedValue) {
             super(service, sessionId);
             mClient = client;
             mAutofillSessionId = autofillSessionId;
             mFocusedId = focusedId;
+            mFocusedValue = focusedValue;
         }
 
         @Override // from MyPendingRequest
@@ -243,7 +249,7 @@
                     final IBinder realClient = resultData
                             .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
                     remoteService.mService.onAutofillRequest(mSessionId, realClient,
-                            mAutofillSessionId, mFocusedId);
+                            mAutofillSessionId, mFocusedId, mFocusedValue, mRequestTime);
                 }
             };
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f3704d8..c4d2a91 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -22,6 +22,7 @@
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.annotation.NonNull;
 import android.app.ActivityThread;
 import android.app.INotificationManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -55,6 +56,9 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.IStorageManager;
+import android.provider.Settings;
+import android.sysprop.VoldProperties;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Slog;
@@ -120,6 +124,7 @@
 import com.android.server.role.RoleManagerService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
+import com.android.server.signedconfig.SignedConfigService;
 import com.android.server.soundtrigger.SoundTriggerService;
 import com.android.server.stats.StatsCompanionService;
 import com.android.server.statusbar.StatusBarManagerService;
@@ -654,7 +659,7 @@
         traceEnd();
 
         // Only run "core" apps if we're encrypting the device.
-        String cryptState = SystemProperties.get("vold.decrypt");
+        String cryptState = VoldProperties.decrypt().orElse("");
         if (ENCRYPTING_STATE.equals(cryptState)) {
             Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
             mOnlyCore = true;
@@ -796,10 +801,6 @@
         boolean disableSystemTextClassifier = SystemProperties.getBoolean(
                 "config.disable_systemtextclassifier", false);
 
-        //TODO(b/111276913): temporarily disabled until the manager is properly implemented to
-        // ignore events when disabled and buffer when enabled
-        boolean disableIntelligence = SystemProperties.getBoolean(
-                "config.disable_intelligence", true);
         boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
                 false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -925,7 +926,7 @@
             ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
             mSensorServiceStart = null;
             wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
-                    new PhoneWindowManager(), mWindowManagerGlobalLock);
+                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
             ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                     DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
@@ -994,6 +995,11 @@
             traceBeginAndSlog("PinnerService");
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
+
+            traceBeginAndSlog("SignedConfigService");
+            SignedConfigService.registerUpdateReceiver(mSystemContext);
+            traceEnd();
+
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
@@ -1130,13 +1136,7 @@
                 traceEnd();
             }
 
-            if (!disableIntelligence) {
-                traceBeginAndSlog("StartIntelligenceService");
-                mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
-                traceEnd();
-            } else {
-                Slog.d(TAG, "IntelligenceService disabled");
-            }
+            startIntelligenceService(context);
 
             // NOTE: ClipboardService indirectly depends on IntelligenceService
             traceBeginAndSlog("StartClipboardService");
@@ -2100,6 +2100,37 @@
         }, BOOT_TIMINGS_TRACE_LOG);
     }
 
+    private void startIntelligenceService(@NonNull Context context) {
+
+        // First check if it was explicitly enabled by Settings
+        boolean explicitlySupported = false;
+        final String settings = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED);
+        if (settings != null) {
+            explicitlySupported = Boolean.parseBoolean(settings);
+            if (explicitlySupported) {
+                Slog.d(TAG, "IntelligenceService explicitly enabled by Settings");
+            } else {
+                Slog.d(TAG, "IntelligenceService explicitly disabled by Settings");
+                return;
+            }
+        }
+
+        // Then check if OEM overlaid the resource that defines the service.
+        if (!explicitlySupported) {
+            final String serviceName = context
+                    .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService);
+            if (TextUtils.isEmpty(serviceName)) {
+                Slog.d(TAG, "IntelligenceService disabled because config resource is not overlaid");
+                return;
+            }
+        }
+
+        traceBeginAndSlog("StartIntelligenceService");
+        mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
+        traceEnd();
+    }
+
     static final void startSystemUi(Context context, WindowManagerService windowManager) {
         Intent intent = new Intent();
         intent.setComponent(new ComponentName("com.android.systemui",
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
new file mode 100644
index 0000000..5342efa
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.security.SecureRandom;
+import java.util.Optional;
+
+/** Tests for {@link RecoverableKeyStoreSecondaryKeyManager}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
+public class RecoverableKeyStoreSecondaryKeyManagerTest {
+    private static final String BACKUP_KEY_ALIAS_PREFIX =
+            "com.android.server.backup/recoverablekeystore/";
+    private static final int BITS_PER_BYTE = 8;
+    private static final int BACKUP_KEY_SUFFIX_LENGTH_BYTES = 128 / BITS_PER_BYTE;
+    private static final int HEX_PER_BYTE = 2;
+    private static final int BACKUP_KEY_ALIAS_LENGTH =
+            BACKUP_KEY_ALIAS_PREFIX.length() + BACKUP_KEY_SUFFIX_LENGTH_BYTES * HEX_PER_BYTE;
+    private static final String NONEXISTENT_KEY_ALIAS = "NONEXISTENT_KEY_ALIAS";
+
+    private RecoverableKeyStoreSecondaryKeyManager mRecoverableKeyStoreSecondaryKeyManager;
+    private Context mContext;
+
+    /** Create a new {@link RecoverableKeyStoreSecondaryKeyManager} to use in tests. */
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+
+        mRecoverableKeyStoreSecondaryKeyManager =
+                new RecoverableKeyStoreSecondaryKeyManager(
+                        RecoveryController.getInstance(mContext), new SecureRandom());
+    }
+
+    /** Reset the {@link ShadowRecoveryController}. */
+    @After
+    public void tearDown() throws Exception {
+        ShadowRecoveryController.reset();
+    }
+
+    /** The generated key should always have the prefix {@code BACKUP_KEY_ALIAS_PREFIX}. */
+    @Test
+    public void generate_generatesKeyWithExpectedPrefix() throws Exception {
+        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+        assertThat(key.getAlias()).startsWith(BACKUP_KEY_ALIAS_PREFIX);
+    }
+
+    /** The generated key should always have length {@code BACKUP_KEY_ALIAS_LENGTH}. */
+    @Test
+    public void generate_generatesKeyWithExpectedLength() throws Exception {
+        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+        assertThat(key.getAlias()).hasLength(BACKUP_KEY_ALIAS_LENGTH);
+    }
+
+    /** Ensure that hidden API exceptions are rethrown when generating keys. */
+    @Test
+    public void generate_encounteringHiddenApiException_rethrowsException() {
+        ShadowRecoveryController.setThrowsInternalError(true);
+
+        assertThrows(
+                InternalRecoveryServiceException.class,
+                mRecoverableKeyStoreSecondaryKeyManager::generate);
+    }
+
+    /** Ensure that retrieved keys correspond to those generated earlier. */
+    @Test
+    public void get_getsKeyGeneratedByController() throws Exception {
+        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+        Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
+                mRecoverableKeyStoreSecondaryKeyManager.get(key.getAlias());
+
+        assertThat(retrievedKey.isPresent()).isTrue();
+        assertThat(retrievedKey.get().getAlias()).isEqualTo(key.getAlias());
+        assertThat(retrievedKey.get().getSecretKey()).isEqualTo(key.getSecretKey());
+    }
+
+    /**
+     * Ensure that a call to {@link RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)}
+     * for nonexistent aliases returns an emtpy {@link Optional}.
+     */
+    @Test
+    public void get_forNonExistentKey_returnsEmptyOptional() throws Exception {
+        Optional<RecoverableKeyStoreSecondaryKey> retrievedKey =
+                mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
+
+        assertThat(retrievedKey.isPresent()).isFalse();
+    }
+
+    /**
+     * Ensure that exceptions occurring during {@link
+     * RecoverableKeyStoreSecondaryKeyManager#get(java.lang.String)} are not rethrown.
+     */
+    @Test
+    public void get_encounteringInternalException_doesNotPropagateException() throws Exception {
+        ShadowRecoveryController.setThrowsInternalError(true);
+
+        // Should not throw exception
+        mRecoverableKeyStoreSecondaryKeyManager.get(NONEXISTENT_KEY_ALIAS);
+    }
+
+    /** Ensure that keys are correctly removed from the store. */
+    @Test
+    public void remove_removesKeyFromRecoverableStore() throws Exception {
+        RecoverableKeyStoreSecondaryKey key = mRecoverableKeyStoreSecondaryKeyManager.generate();
+
+        mRecoverableKeyStoreSecondaryKeyManager.remove(key.getAlias());
+
+        assertThat(RecoveryController.getInstance(mContext).getAliases())
+                .doesNotContain(key.getAlias());
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
new file mode 100644
index 0000000..89977f8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey.Status;
+import com.android.server.backup.testing.CryptoTestUtils;
+import com.android.server.testing.shadows.ShadowInternalRecoveryServiceException;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link RecoverableKeyStoreSecondaryKey}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowInternalRecoveryServiceException.class})
+public class RecoverableKeyStoreSecondaryKeyTest {
+    private static final String TEST_ALIAS = "test";
+    private static final int NONEXISTENT_STATUS_CODE = 42;
+
+    private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+    private SecretKey mGeneratedSecretKey;
+    private Context mContext;
+
+    /** Instantiate a {@link RecoverableKeyStoreSecondaryKey} to use in tests. */
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+        mGeneratedSecretKey = CryptoTestUtils.generateAesKey();
+        mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, mGeneratedSecretKey);
+    }
+
+    /** Reset the {@link ShadowRecoveryController}. */
+    @After
+    public void tearDown() throws Exception {
+        ShadowRecoveryController.reset();
+    }
+
+    /**
+     * Checks that {@link RecoverableKeyStoreSecondaryKey#getAlias()} returns the value supplied in
+     * the constructor.
+     */
+    @Test
+    public void getAlias() {
+        String alias = mSecondaryKey.getAlias();
+
+        assertThat(alias).isEqualTo(TEST_ALIAS);
+    }
+
+    /**
+     * Checks that {@link RecoverableKeyStoreSecondaryKey#getSecretKey()} returns the value supplied
+     * in the constructor.
+     */
+    @Test
+    public void getSecretKey() {
+        SecretKey secretKey = mSecondaryKey.getSecretKey();
+
+        assertThat(secretKey).isEqualTo(mGeneratedSecretKey);
+    }
+
+    /**
+     * Checks that passing a secret key that is null to the constructor throws an exception.
+     */
+    @Test
+    public void constructor_withNullSecretKey_throwsNullPointerException() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new RecoverableKeyStoreSecondaryKey(TEST_ALIAS, null));
+    }
+
+    /**
+     * Checks that passing an alias that is null to the constructor throws an exception.
+     */
+    @Test
+    public void constructor_withNullAlias_throwsNullPointerException() {
+        assertThrows(
+                NullPointerException.class,
+                () -> new RecoverableKeyStoreSecondaryKey(null, mGeneratedSecretKey));
+    }
+
+    /** Checks that the synced status is returned correctly. */
+    @Test
+    public void getStatus_whenSynced_returnsSynced() throws Exception {
+        setStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+
+        int status = mSecondaryKey.getStatus(mContext);
+
+        assertThat(status).isEqualTo(Status.SYNCED);
+    }
+
+    /** Checks that the in progress sync status is returned correctly. */
+    @Test
+    public void getStatus_whenNotSynced_returnsNotSynced() throws Exception {
+        setStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+
+        int status = mSecondaryKey.getStatus(mContext);
+
+        assertThat(status).isEqualTo(Status.NOT_SYNCED);
+    }
+
+    /** Checks that the failure status is returned correctly. */
+    @Test
+    public void getStatus_onPermanentFailure_returnsDestroyed() throws Exception {
+        setStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+
+        int status = mSecondaryKey.getStatus(mContext);
+
+        assertThat(status).isEqualTo(Status.DESTROYED);
+    }
+
+    /** Checks that an unknown status results in {@code NOT_SYNCED} being returned. */
+    @Test
+    public void getStatus_forUnknownStatusCode_returnsNotSynced() throws Exception {
+        setStatus(NONEXISTENT_STATUS_CODE);
+
+        int status = mSecondaryKey.getStatus(mContext);
+
+        assertThat(status).isEqualTo(Status.NOT_SYNCED);
+    }
+
+    /** Checks that an internal error results in {@code NOT_SYNCED} being returned. */
+    @Test
+    public void getStatus_onInternalError_returnsNotSynced() throws Exception {
+        ShadowRecoveryController.setThrowsInternalError(true);
+
+        int status = mSecondaryKey.getStatus(mContext);
+
+        assertThat(status).isEqualTo(Status.NOT_SYNCED);
+    }
+
+    private void setStatus(int status) throws Exception {
+        ShadowRecoveryController.setRecoveryStatus(TEST_ALIAS, status);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
new file mode 100644
index 0000000..48216f8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.security.SecureRandom;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link TertiaryKeyGenerator}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TertiaryKeyGeneratorTest {
+    private static final String KEY_ALGORITHM = "AES";
+    private static final int KEY_SIZE_BITS = 256;
+
+    private TertiaryKeyGenerator mTertiaryKeyGenerator;
+
+    /** Instantiate a new {@link TertiaryKeyGenerator} for use in tests. */
+    @Before
+    public void setUp() {
+        mTertiaryKeyGenerator = new TertiaryKeyGenerator(new SecureRandom());
+    }
+
+    /** Generated keys should be AES keys. */
+    @Test
+    public void generate_generatesAESKeys() {
+        SecretKey secretKey = mTertiaryKeyGenerator.generate();
+
+        assertThat(secretKey.getAlgorithm()).isEqualTo(KEY_ALGORITHM);
+    }
+
+    /** Generated keys should be 256 bits in size. */
+    @Test
+    public void generate_generates256BitKeys() {
+        SecretKey secretKey = mTertiaryKeyGenerator.generate();
+
+        assertThat(secretKey.getEncoded()).hasLength(KEY_SIZE_BITS / 8);
+    }
+
+    /**
+     * Subsequent calls to {@link TertiaryKeyGenerator#generate()} should generate different keys.
+     */
+    @Test
+    public void generate_generatesNewKeys() {
+        SecretKey key1 = mTertiaryKeyGenerator.generate();
+        SecretKey key2 = mTertiaryKeyGenerator.generate();
+
+        assertThat(key1).isNotEqualTo(key2);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
new file mode 100644
index 0000000..49bb410
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.backup.encryption.keys;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+/** Tests for {@link TertiaryKeyRotationTracker}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class TertiaryKeyRotationTrackerTest {
+    private static final String PACKAGE_1 = "com.package.one";
+    private static final int NUMBER_OF_BACKUPS_BEFORE_ROTATION = 31;
+
+    private TertiaryKeyRotationTracker mTertiaryKeyRotationTracker;
+
+    /** Instantiate a {@link TertiaryKeyRotationTracker} for use in tests. */
+    @Before
+    public void setUp() {
+        mTertiaryKeyRotationTracker = newInstance();
+    }
+
+    /** New packages should not be due for key rotation. */
+    @Test
+    public void isKeyRotationDue_forNewPackage_isFalse() {
+        // Simulate a new package by not calling simulateBackups(). As a result, PACKAGE_1 hasn't
+        // been seen by mTertiaryKeyRotationTracker before.
+        boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+        assertThat(keyRotationDue).isFalse();
+    }
+
+    /**
+     * Key rotation should not be due after less than {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION}
+     * backups.
+     */
+    @Test
+    public void isKeyRotationDue_afterLessThanRotationAmountBackups_isFalse() {
+        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION - 1);
+
+        boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+        assertThat(keyRotationDue).isFalse();
+    }
+
+    /** Key rotation should be due after {@code NUMBER_OF_BACKUPS_BEFORE_ROTATION} backups. */
+    @Test
+    public void isKeyRotationDue_afterRotationAmountBackups_isTrue() {
+        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+        boolean keyRotationDue = mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1);
+
+        assertThat(keyRotationDue).isTrue();
+    }
+
+    /**
+     * A call to {@link TertiaryKeyRotationTracker#resetCountdown(String)} should make sure no key
+     * rotation is due.
+     */
+    @Test
+    public void resetCountdown_makesKeyRotationNotDue() {
+        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+        mTertiaryKeyRotationTracker.resetCountdown(PACKAGE_1);
+
+        assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
+    }
+
+    /**
+     * New instances of {@link TertiaryKeyRotationTracker} should read state about the number of
+     * backups from disk.
+     */
+    @Test
+    public void isKeyRotationDue_forNewInstance_readsStateFromDisk() {
+        simulateBackups(PACKAGE_1, NUMBER_OF_BACKUPS_BEFORE_ROTATION);
+
+        boolean keyRotationDueForNewInstance = newInstance().isKeyRotationDue(PACKAGE_1);
+
+        assertThat(keyRotationDueForNewInstance).isTrue();
+    }
+
+    /**
+     * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should mark all previously
+     * seen packages for rotation.
+     */
+    @Test
+    public void markAllForRotation_marksSeenPackagesForKeyRotation() {
+        simulateBackups(PACKAGE_1, /*numberOfBackups=*/ 1);
+
+        mTertiaryKeyRotationTracker.markAllForRotation();
+
+        assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isTrue();
+    }
+
+    /**
+     * A call to {@link TertiaryKeyRotationTracker#markAllForRotation()} should not mark any new
+     * packages for rotation.
+     */
+    @Test
+    public void markAllForRotation_doesNotMarkUnseenPackages() {
+        mTertiaryKeyRotationTracker.markAllForRotation();
+
+        assertThat(mTertiaryKeyRotationTracker.isKeyRotationDue(PACKAGE_1)).isFalse();
+    }
+
+    private void simulateBackups(String packageName, int numberOfBackups) {
+        while (numberOfBackups > 0) {
+            mTertiaryKeyRotationTracker.recordBackup(packageName);
+            numberOfBackups--;
+        }
+    }
+
+    private static TertiaryKeyRotationTracker newInstance() {
+        return TertiaryKeyRotationTracker.getInstance(RuntimeEnvironment.application);
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
new file mode 100644
index 0000000..9c06d81
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.testing.shadows;
+
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Shadow {@link InternalRecoveryServiceException}. */
+@Implements(InternalRecoveryServiceException.class)
+public class ShadowInternalRecoveryServiceException {
+    private String mMessage;
+
+    @Implementation
+    public void __constructor__(String message) {
+        mMessage = message;
+    }
+
+    @Implementation
+    public void __constructor__(String message, Throwable cause) {
+        mMessage = message;
+    }
+
+    @Implementation
+    public String getMessage() {
+        return mMessage;
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
new file mode 100644
index 0000000..7dad8a4
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
@@ -0,0 +1,152 @@
+/*
+ * 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.testing.shadows;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.google.common.collect.ImmutableList;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.lang.reflect.Constructor;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.crypto.KeyGenerator;
+
+/**
+ * Shadow of {@link RecoveryController}.
+ *
+ * <p>Instead of generating keys via the {@link RecoveryController}, this shadow generates them in
+ * memory.
+ */
+@Implements(RecoveryController.class)
+public class ShadowRecoveryController {
+    private static final String KEY_GENERATOR_ALGORITHM = "AES";
+    private static final int KEY_SIZE_BITS = 256;
+
+    private static boolean sIsSupported = true;
+    private static boolean sThrowsInternalError = false;
+    private static HashMap<String, Key> sKeysByAlias = new HashMap<>();
+    private static HashMap<String, Integer> sKeyStatusesByAlias = new HashMap<>();
+
+    @Implementation
+    public void __constructor__() {
+        // do not throw
+    }
+
+    @Implementation
+    public static RecoveryController getInstance(Context context) {
+        // Call non-public constructor.
+        try {
+            Constructor<RecoveryController> constructor = RecoveryController.class.getConstructor();
+            return constructor.newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Implementation
+    public static boolean isRecoverableKeyStoreEnabled(Context context) {
+        return sIsSupported;
+    }
+
+    @Implementation
+    public Key generateKey(String alias)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        maybeThrowError();
+        KeyGenerator keyGenerator;
+        try {
+            keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+        } catch (NoSuchAlgorithmException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        keyGenerator.init(KEY_SIZE_BITS);
+        Key key = keyGenerator.generateKey();
+        sKeysByAlias.put(alias, key);
+        sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+        return key;
+    }
+
+    @Implementation
+    public Key getKey(String alias)
+            throws InternalRecoveryServiceException, UnrecoverableKeyException {
+        return sKeysByAlias.get(alias);
+    }
+
+    @Implementation
+    public void removeKey(String alias) throws InternalRecoveryServiceException {
+        sKeyStatusesByAlias.remove(alias);
+        sKeysByAlias.remove(alias);
+    }
+
+    @Implementation
+    public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
+        maybeThrowError();
+        return sKeyStatusesByAlias.getOrDefault(
+                alias, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+    }
+
+    @Implementation
+    public List<String> getAliases() throws InternalRecoveryServiceException {
+        return ImmutableList.copyOf(sKeyStatusesByAlias.keySet());
+    }
+
+    private static void maybeThrowError() throws InternalRecoveryServiceException {
+        if (sThrowsInternalError) {
+            throw new InternalRecoveryServiceException("test error");
+        }
+    }
+
+    /** Sets the recovery status of the key with {@code alias} to {@code status}. */
+    public static void setRecoveryStatus(String alias, int status) {
+        sKeyStatusesByAlias.put(alias, status);
+    }
+
+    /** Sets all existing keys to being synced. */
+    public static void syncAllKeys() {
+        for (String alias : sKeysByAlias.keySet()) {
+            sKeyStatusesByAlias.put(alias, RecoveryController.RECOVERY_STATUS_SYNCED);
+        }
+    }
+
+    public static void setThrowsInternalError(boolean throwsInternalError) {
+        ShadowRecoveryController.sThrowsInternalError = throwsInternalError;
+    }
+
+    public static void setIsSupported(boolean isSupported) {
+        ShadowRecoveryController.sIsSupported = isSupported;
+    }
+
+    @Resetter
+    public static void reset() {
+        sIsSupported = true;
+        sThrowsInternalError = false;
+        sKeysByAlias.clear();
+        sKeyStatusesByAlias.clear();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
new file mode 100644
index 0000000..b2ec835
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -0,0 +1,842 @@
+/*
+ * 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.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
+
+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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.job.JobInfo;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.controllers.QuotaController.TimingSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class QuotaControllerTest {
+    private static final long SECOND_IN_MILLIS = 1000L;
+    private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+    private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+    private static final String TAG_CLEANUP = "*job.cleanup*";
+    private static final String TAG_QUOTA_CHECK = "*job.quota_check*";
+    private static final long IN_QUOTA_BUFFER_MILLIS = 30 * SECOND_IN_MILLIS;
+    private static final int CALLING_UID = 1000;
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+    private static final int SOURCE_USER_ID = 0;
+
+    private BroadcastReceiver mChargingReceiver;
+    private Constants mConstants;
+    private QuotaController mQuotaController;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private JobSchedulerService mJobSchedulerService;
+    @Mock
+    private UsageStatsManagerInternal mUsageStatsManager;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+        // Make sure constants turn on QuotaController.
+        mConstants = new Constants();
+        mConstants.USE_HEARTBEATS = false;
+
+        // Called in StateController constructor.
+        when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+        when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        // Called in QuotaController constructor.
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        doReturn(mock(BatteryManagerInternal.class))
+                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+        doReturn(mUsageStatsManager)
+                .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+        // Used in JobStatus.
+        doReturn(mock(PackageManagerInternal.class))
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+        // Freeze the clocks at this moment in time
+        JobSchedulerService.sSystemClock =
+                Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sUptimeMillisClock =
+                Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+        // Initialize real objects.
+        // Capture the listeners.
+        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        mQuotaController = new QuotaController(mJobSchedulerService);
+
+        verify(mContext).registerReceiver(receiverCaptor.capture(), any());
+        mChargingReceiver = receiverCaptor.getValue();
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private Clock getAdvancedClock(Clock clock, long incrementMs) {
+        return Clock.offset(clock, Duration.ofMillis(incrementMs));
+    }
+
+    private void advanceElapsedClock(long incrementMs) {
+        JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+                JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+    }
+
+    private void setCharging() {
+        Intent intent = new Intent(BatteryManager.ACTION_CHARGING);
+        mChargingReceiver.onReceive(mContext, intent);
+    }
+
+    private void setDischarging() {
+        Intent intent = new Intent(BatteryManager.ACTION_DISCHARGING);
+        mChargingReceiver.onReceive(mContext, intent);
+    }
+
+    private void setStandbyBucket(int bucketIndex) {
+        int bucket;
+        switch (bucketIndex) {
+            case ACTIVE_INDEX:
+                bucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+                break;
+            case WORKING_INDEX:
+                bucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+                break;
+            case FREQUENT_INDEX:
+                bucket = UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+                break;
+            case RARE_INDEX:
+                bucket = UsageStatsManager.STANDBY_BUCKET_RARE;
+                break;
+            default:
+                bucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
+        }
+        when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID),
+                anyLong())).thenReturn(bucket);
+    }
+
+    private void setStandbyBucket(int bucketIndex, JobStatus job) {
+        setStandbyBucket(bucketIndex);
+        job.setStandbyBucket(bucketIndex);
+    }
+
+    private JobStatus createJobStatus(String testTag, int jobId) {
+        JobInfo jobInfo = new JobInfo.Builder(jobId,
+                new ComponentName(mContext, "TestQuotaJobService"))
+                .setMinimumLatency(Math.abs(jobId) + 1)
+                .build();
+        return JobStatus.createFromJobInfo(
+                jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+    }
+
+    private TimingSession createTimingSession(long start, long duration, int count) {
+        return new TimingSession(start, start + duration, count);
+    }
+
+    @Test
+    public void testSaveTimingSession() {
+        assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
+
+        List<TimingSession> expected = new ArrayList<>();
+        TimingSession one = new TimingSession(1, 10, 1);
+        TimingSession two = new TimingSession(11, 20, 2);
+        TimingSession thr = new TimingSession(21, 30, 3);
+
+        mQuotaController.saveTimingSession(0, "com.android.test", one);
+        expected.add(one);
+        assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+
+        mQuotaController.saveTimingSession(0, "com.android.test", two);
+        expected.add(two);
+        assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+
+        mQuotaController.saveTimingSession(0, "com.android.test", thr);
+        expected.add(thr);
+        assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+    }
+
+    @Test
+    public void testDeleteObsoleteSessionsLocked() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        TimingSession one = createTimingSession(
+                now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
+        TimingSession two = createTimingSession(
+                now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+        TimingSession thr = createTimingSession(
+                now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+        // Overlaps 24 hour boundary.
+        TimingSession fou = createTimingSession(
+                now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1);
+        // Way past the 24 hour boundary.
+        TimingSession fiv = createTimingSession(
+                now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4);
+        List<TimingSession> expected = new ArrayList<>();
+        // Added in correct (chronological) order.
+        expected.add(fou);
+        expected.add(thr);
+        expected.add(two);
+        expected.add(one);
+        mQuotaController.saveTimingSession(0, "com.android.test", fiv);
+        mQuotaController.saveTimingSession(0, "com.android.test", fou);
+        mQuotaController.saveTimingSession(0, "com.android.test", thr);
+        mQuotaController.saveTimingSession(0, "com.android.test", two);
+        mQuotaController.saveTimingSession(0, "com.android.test", one);
+
+        mQuotaController.deleteObsoleteSessionsLocked();
+
+        assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+    }
+
+    @Test
+    public void testGetTrailingExecutionTimeLocked_NoTimer() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Added in chronological order.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(
+                        now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(
+                        now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
+
+        assertEquals(0, mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                MINUTE_IN_MILLIS));
+        assertEquals(2 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        3 * MINUTE_IN_MILLIS));
+        assertEquals(4 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        5 * MINUTE_IN_MILLIS));
+        assertEquals(4 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        49 * MINUTE_IN_MILLIS));
+        assertEquals(5 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        50 * MINUTE_IN_MILLIS));
+        assertEquals(6 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        HOUR_IN_MILLIS));
+        assertEquals(11 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        2 * HOUR_IN_MILLIS));
+        assertEquals(12 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        3 * HOUR_IN_MILLIS));
+        assertEquals(22 * MINUTE_IN_MILLIS,
+                mQuotaController.getTrailingExecutionTimeLocked(0, "com.android.test",
+                        6 * HOUR_IN_MILLIS));
+    }
+
+    @Test
+    public void testMaybeScheduleCleanupAlarmLocked() {
+        // No sessions saved yet.
+        mQuotaController.maybeScheduleCleanupAlarmLocked();
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
+
+        // Test with only one timing session saved.
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1));
+        mQuotaController.maybeScheduleCleanupAlarmLocked();
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+
+        // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleCleanupAlarmLocked();
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_WorkingSet() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        // Working set window size is 2 hours.
+        final int standbyBucket = WORKING_INDEX;
+
+        // No sessions saved yet.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions out of window.
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions in window but still in quota.
+        final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
+        // Counting backwards, the quota will come back one minute before the end.
+        final long expectedAlarmTime =
+                end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS;
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Add some more sessions, but still in quota.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test when out of quota.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Alarm already scheduled, so make sure it's not scheduled again.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_Frequent() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        // Frequent window size is 8 hours.
+        final int standbyBucket = FREQUENT_INDEX;
+
+        // No sessions saved yet.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions out of window.
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions in window but still in quota.
+        final long start = now - (6 * HOUR_IN_MILLIS);
+        final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS;
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Add some more sessions, but still in quota.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test when out of quota.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Alarm already scheduled, so make sure it's not scheduled again.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_Rare() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        // Rare window size is 24 hours.
+        final int standbyBucket = RARE_INDEX;
+
+        // No sessions saved yet.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions out of window.
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions in window but still in quota.
+        final long start = now - (6 * HOUR_IN_MILLIS);
+        // Counting backwards, the first minute in the session is over the allowed time, so it
+        // needs to be excluded.
+        final long expectedAlarmTime =
+                start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS + IN_QUOTA_BUFFER_MILLIS;
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Add some more sessions, but still in quota.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test when out of quota.
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1));
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Alarm already scheduled, so make sure it's not scheduled again.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
+    /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_BucketChange() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        // Affects rare bucket
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3));
+        // Affects frequent and rare buckets
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
+        // Affects working, frequent, and rare buckets
+        final long outOfQuotaTime = now - HOUR_IN_MILLIS;
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10));
+        // Affects all buckets
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3));
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        // Start in ACTIVE bucket.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+        inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any());
+        inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
+
+        // And down from there.
+        final long expectedWorkingAlarmTime =
+                outOfQuotaTime + (2 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS;
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        final long expectedFrequentAlarmTime =
+                outOfQuotaTime + (8 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS;
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        final long expectedRareAlarmTime =
+                outOfQuotaTime + (24 * HOUR_IN_MILLIS) + IN_QUOTA_BUFFER_MILLIS;
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // And back up again.
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+        inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
+                any());
+        inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
+    }
+
+    /** Tests that QuotaController doesn't throttle if throttling is turned off. */
+    @Test
+    public void testThrottleToggling() throws Exception {
+        setDischarging();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+                        10 * MINUTE_IN_MILLIS, 4));
+        JobStatus jobStatus = createJobStatus("testThrottleToggling", 1);
+        setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        mConstants.USE_HEARTBEATS = true;
+        mQuotaController.onConstantsUpdatedLocked();
+        Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
+        assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        mConstants.USE_HEARTBEATS = false;
+        mQuotaController.onConstantsUpdatedLocked();
+        Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
+        assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+    }
+
+    @Test
+    public void testConstantsUpdating_ValidValues() {
+        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
+
+        mQuotaController.onConstantsUpdatedLocked();
+
+        assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+        assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
+        assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
+        assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
+        assertEquals(45 * MINUTE_IN_MILLIS,
+                mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
+        assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+    }
+
+    @Test
+    public void testConstantsUpdating_InvalidValues() {
+        // Test negatives
+        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
+
+        mQuotaController.onConstantsUpdatedLocked();
+
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+        assertEquals(0, mQuotaController.getInQuotaBufferMs());
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
+        assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+
+        // Test larger than a day. Controller should cap at one day.
+        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
+
+        mQuotaController.onConstantsUpdatedLocked();
+
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+        assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
+        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+    }
+
+    /** Tests that TimingSessions aren't saved when the device is charging. */
+    @Test
+    public void testTimerTracking_Charging() {
+        setCharging();
+
+        JobStatus jobStatus = createJobStatus("testTimerTracking_Charging", 1);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    /** Tests that TimingSessions are saved properly when the device is discharging. */
+    @Test
+    public void testTimerTracking_Discharging() {
+        setDischarging();
+
+        JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        List<TimingSession> expected = new ArrayList<>();
+
+        long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+        expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // Test overlapping jobs.
+        JobStatus jobStatus2 = createJobStatus("testTimerTracking_Discharging", 2);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+
+        JobStatus jobStatus3 = createJobStatus("testTimerTracking_Discharging", 3);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.prepareForExecutionLocked(jobStatus2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.prepareForExecutionLocked(jobStatus3);
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+        expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    /**
+     * Tests that TimingSessions are saved properly when the device alternates between
+     * charging and discharging.
+     */
+    @Test
+    public void testTimerTracking_ChargingAndDischarging() {
+        JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+        JobStatus jobStatus3 = createJobStatus("testTimerTracking_ChargingAndDischarging", 3);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+        List<TimingSession> expected = new ArrayList<>();
+
+        // A job starting while charging. Only the portion that runs during the discharging period
+        // should be counted.
+        setCharging();
+
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        setDischarging();
+        long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+
+        // One job starts while discharging, spans a charging session, and ends after the charging
+        // session. Only the portions during the discharging periods should be counted. This should
+        // result in two TimingSessions. A second job starts while discharging and ends within the
+        // charging session. Only the portion during the first discharging portion should be
+        // counted. A third job starts and ends within the charging session. The third job
+        // shouldn't be included in either job count.
+        setDischarging();
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.prepareForExecutionLocked(jobStatus2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        setCharging();
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+        mQuotaController.prepareForExecutionLocked(jobStatus3);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        setDischarging();
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // A job starting while discharging and ending while charging. Only the portion that runs
+        // during the discharging period should be counted.
+        setDischarging();
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+        mQuotaController.prepareForExecutionLocked(jobStatus2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setCharging();
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    /**
+     * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches
+     * its quota.
+     */
+    @Test
+    public void testTracking_OutOfQuota() {
+        JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+        // Now the package only has two seconds to run.
+        final long remainingTimeMs = 2 * SECOND_IN_MILLIS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+                        10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+
+        // Start the job.
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(remainingTimeMs);
+
+        // Wait for some extra time to allow for job processing.
+        verify(mJobSchedulerService,
+                timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+    }
+
+    /**
+     * Tests that a job is properly handled when it's at the edge of its quota and the old quota is
+     * being phased out.
+     */
+    @Test
+    public void testTracking_RollingQuota() {
+        JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
+        mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+        Handler handler = mQuotaController.getHandler();
+        spyOn(handler);
+
+        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        final long remainingTimeMs = SECOND_IN_MILLIS;
+        // The package only has one second to run, but this session is at the edge of the rolling
+        // window, so as the package "reaches its quota" it will have more to keep running.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - 2 * HOUR_IN_MILLIS,
+                        10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+
+        assertEquals(remainingTimeMs, mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
+        // Start the job.
+        mQuotaController.prepareForExecutionLocked(jobStatus);
+        advanceElapsedClock(remainingTimeMs);
+
+        // Wait for some extra time to allow for job processing.
+        verify(mJobSchedulerService,
+                timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+                .onControllerStateChanged();
+        assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        // The job used up the remaining quota, but in that time, the same amount of time in the
+        // old TimingSession also fell out of the quota window, so it should still have the same
+        // amount of remaining time left its quota.
+        assertEquals(remainingTimeMs,
+                mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+        verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(remainingTimeMs));
+    }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 746c453..cf4d3a8 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -66,6 +66,7 @@
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
new file mode 100644
index 0000000..f70efdf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BinderCallsStatsServiceTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Binder;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.BinderInternal.CallSession;
+import com.android.server.BinderCallsStatsService.WorkSourceProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class BinderCallsStatsServiceTest {
+    @Test
+    public void weTrustOurselves() {
+        WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+            protected int getCallingUid() {
+                return Process.myUid();
+            }
+
+            protected int getCallingWorkSourceUid() {
+                return 1;
+            }
+        };
+        workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+        assertEquals(1, workSourceProvider.resolveWorkSourceUid());
+    }
+
+    @Test
+    public void workSourceSetIfCallerHasPermission() {
+        WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+            protected int getCallingUid() {
+                // System process uid which as UPDATE_DEVICE_STATS.
+                return 1001;
+            }
+
+            protected int getCallingWorkSourceUid() {
+                return 1;
+            }
+        };
+        workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+        assertEquals(1, workSourceProvider.resolveWorkSourceUid());
+    }
+
+    @Test
+    public void workSourceResolvedToCallingUid() {
+        WorkSourceProvider workSourceProvider = new WorkSourceProvider() {
+            protected int getCallingUid() {
+                // UID without permissions.
+                return Integer.MAX_VALUE;
+            }
+
+            protected int getCallingWorkSourceUid() {
+                return 1;
+            }
+        };
+        workSourceProvider.systemReady(InstrumentationRegistry.getContext());
+
+        assertEquals(Integer.MAX_VALUE, workSourceProvider.resolveWorkSourceUid());
+    }
+
+    @Test
+    public void workSourceSet() {
+        TestObserver observer = new TestObserver();
+        observer.callStarted(new Binder(), 0);
+        assertEquals(true, observer.workSourceSet);
+    }
+
+    static class TestObserver extends BinderCallsStatsService.BinderCallsObserver {
+        public boolean workSourceSet = false;
+
+        TestObserver() {
+            super(new NoopObserver(), new WorkSourceProvider());
+        }
+
+        @Override
+        protected void setThreadLocalWorkSourceUid(int uid) {
+            workSourceSet = true;
+        }
+    }
+
+
+    static class NoopObserver implements BinderInternal.Observer {
+        @Override
+        public CallSession callStarted(Binder binder, int code) {
+            return null;
+        }
+
+        @Override
+        public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+        }
+
+        @Override
+        public void callThrewException(CallSession s, Exception exception) {
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 81107cf..2a78b6f 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -17,9 +17,10 @@
 package com.android.server;
 
 
-import static org.mockito.Mockito.when;
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
@@ -134,4 +135,44 @@
         mContext.sendBroadcast(intentUnplugged);
         assertThat(deviceState.isCharging()).isFalse();
     }
+
+    @Test
+    public void correctlyTracksTimeOnBattery() throws Exception {
+        CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+        when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+        service.onStart();
+        CachedDeviceState.Readonly deviceState =
+                LocalServices.getService(CachedDeviceState.Readonly.class);
+
+        CachedDeviceState.TimeInStateStopwatch stopwatch =
+                deviceState.createTimeOnBatteryStopwatch();
+
+        // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+        assertThat(stopwatch.isRunning()).isFalse();
+        service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        assertThat(stopwatch.isRunning()).isTrue();
+        stopwatch.reset();
+
+        Thread.sleep(100);
+        assertThat(stopwatch.isRunning()).isTrue();
+        assertThat(stopwatch.getMillis()).isAtLeast(100L);
+
+        long timeOnBatteryBeforePluggedIn = stopwatch.getMillis();
+        Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        mContext.sendBroadcast(intentPluggedIn);
+
+        assertThat(stopwatch.getMillis()).isAtLeast(timeOnBatteryBeforePluggedIn);
+        assertThat(stopwatch.isRunning()).isFalse();
+
+        long timeOnBatteryAfterPluggedIn = stopwatch.getMillis();
+        Thread.sleep(20);
+        assertThat(stopwatch.getMillis()).isEqualTo(timeOnBatteryAfterPluggedIn);
+
+        stopwatch.reset();
+        assertThat(stopwatch.getMillis()).isEqualTo(0L);
+        assertThat(stopwatch.isRunning()).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 53711a6..e0ecd3e 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -35,6 +35,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.internal.app.ColorDisplayController;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
@@ -911,7 +912,11 @@
         startService();
         assertAccessibilityTransformActivated(true /* activated */ );
         assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
-        assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+        }
     }
 
     @Test
@@ -926,7 +931,11 @@
         startService();
         assertAccessibilityTransformActivated(true /* activated */ );
         assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
-        assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+        }
     }
 
     @Test
@@ -942,7 +951,11 @@
         startService();
         assertAccessibilityTransformActivated(true /* activated */ );
         assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
-        assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+        }
     }
 
     @Test
@@ -1030,6 +1043,24 @@
     }
 
     /**
+     * Returns whether the color mode is valid on the device the tests are running on.
+     *
+     * @param mode the mode to check
+     */
+    private boolean isColorModeValid(int mode) {
+        final int[] availableColorModes = mContext.getResources().getIntArray(
+            R.array.config_availableColorModes);
+        if (availableColorModes != null) {
+            for (int availableMode : availableColorModes) {
+                if (mode == availableMode) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Convenience method to start {@link #mColorDisplayService}.
      */
     private void startService() {
@@ -1038,7 +1069,6 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mColorDisplayService.onStart();
                 mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
                 mColorDisplayService.onStartUser(mUserId);
             }
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 7cf7df13..c1963da 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -268,10 +268,10 @@
 
     @Test
     public void testGetCurrentStatus() throws RemoteException {
-        int status = Temperature.THROTTLING_WARNING;
+        int status = Temperature.THROTTLING_EMERGENCY;
         Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
         mFakeHal.mCallback.onValues(newSkin);
-        assertEquals(status, mService.mService.getCurrentStatus());
+        assertEquals(status, mService.mService.getCurrentThermalStatus());
     }
 
     @Test
@@ -294,6 +294,6 @@
         assertEquals(0, mService.mService.getCurrentTemperatures().size());
         assertEquals(0,
                 mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
-        assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+        assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9da204f..41d5a1c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -456,6 +456,31 @@
     }
 
     @Test
+    public void testNoBeepForImportanceDefaultInAutomotive() throws Exception {
+        mService.setIsAutomotive(true);
+
+        NotificationRecord r = getBeepyNotification();
+        r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testBeepForImportanceHighInAutomotive() throws Exception {
+        mService.setIsAutomotive(true);
+
+        NotificationRecord r = getBeepyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+        assertTrue(r.isInterruptive());
+    }
+
+    @Test
     public void testNoInterruptionForMin() throws Exception {
         NotificationRecord r = getBeepyNotification();
         r.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index b30bb4b..0681295 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -15,8 +15,9 @@
  */
 package com.android.server.notification;
 
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.contains;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -34,8 +35,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.telecom.TelecomManager;
 import android.support.test.runner.AndroidJUnit4;
+import android.telecom.TelecomManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.UiServiceTestCase;
@@ -211,7 +212,7 @@
         mRecordColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
                 pkg2, 1, "colorized", uid2, uid2, n13,
                 new UserHandle(userId), "", 1999), getDefaultChannel());
-        mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
+        mRecordColorized.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
 
         Notification n14 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setCategory(Notification.CATEGORY_CALL)
@@ -225,11 +226,11 @@
     }
 
     @Test
-    public void testOrdering() throws Exception {
+    public void testOrdering() {
         final List<NotificationRecord> expected = new ArrayList<>();
         expected.add(mRecordColorizedCall);
-        expected.add(mRecordDefaultMedia);
         expected.add(mRecordColorized);
+        expected.add(mRecordDefaultMedia);
         expected.add(mRecordHighCall);
         expected.add(mRecordInlineReply);
         if (mRecordSms != null) {
@@ -250,11 +251,11 @@
 
         Collections.sort(actual, new NotificationComparator(mContext));
 
-        assertEquals(expected, actual);
+        assertThat(actual, contains(expected.toArray()));
     }
 
     @Test
-    public void testMessaging() throws Exception {
+    public void testMessaging() {
         NotificationComparator comp = new NotificationComparator(mContext);
         assertTrue(comp.isImportantMessaging(mRecordInlineReply));
         if (mRecordSms != null) {
@@ -265,7 +266,7 @@
     }
 
     @Test
-    public void testPeople() throws Exception {
+    public void testPeople() {
         NotificationComparator comp = new NotificationComparator(mContext);
         assertTrue(comp.isImportantPeople(mRecordStarredContact));
         assertTrue(comp.isImportantPeople(mRecordContact));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 1d63c57..7be331c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -79,6 +79,7 @@
         resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
         when(mDisplayPolicy.getSystemUiContext()).thenReturn(context);
         when(mDisplayPolicy.hasNavigationBar()).thenReturn(true);
+        when(mDisplayPolicy.hasStatusBar()).thenReturn(true);
 
         final int shortSizeDp =
                 Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 9569c0d5..105f826 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -22,7 +22,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -750,6 +753,64 @@
     }
 
     @Test
+    public void testUsesDisplayOrientationForNoSensorOrientation() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_NOSENSOR;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        final int orientationForDisplay = orientationFromBounds(freeformDisplay.getBounds());
+        final int orientationForTask = orientationFromBounds(mResult.mBounds);
+        assertEquals("Launch bounds orientation should be the same as the display, but"
+                        + " display orientation is "
+                        + ActivityInfo.screenOrientationToString(orientationForDisplay)
+                        + " launch bounds orientation is "
+                        + ActivityInfo.screenOrientationToString(orientationForTask),
+                orientationForDisplay, orientationForTask);
+    }
+
+    @Test
+    public void testRespectsAppRequestedOrientation_Landscape() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, orientationFromBounds(mResult.mBounds));
+    }
+
+    @Test
+    public void testRespectsAppRequestedOrientation_Portrait() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, options, mCurrent, mResult));
+
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, orientationFromBounds(mResult.mBounds));
+    }
+
+    @Test
     public void testDefaultSizeSmallerThanBigScreen() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
@@ -1090,6 +1151,7 @@
         display.setWindowingMode(windowingMode);
         display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080);
         display.getConfiguration().densityDpi = DENSITY_DEFAULT;
+        display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
         return display;
     }
 
@@ -1115,6 +1177,11 @@
         }
     }
 
+    private int orientationFromBounds(Rect bounds) {
+        return bounds.width() > bounds.height() ? SCREEN_ORIENTATION_LANDSCAPE
+                : SCREEN_ORIENTATION_PORTRAIT;
+    }
+
     private static class WindowLayoutBuilder {
         private int mWidth = -1;
         private int mHeight = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 3643457..885a7e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -231,7 +231,7 @@
 
         final Configuration config = new Configuration();
         final WindowConfiguration winConfig = config.windowConfiguration;
-        stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
+        stackController.adjustConfigurationForBounds(bounds,
                 new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
                 false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
                 windowingMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index b3e90de..60a8aeb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -76,7 +76,7 @@
 
     private static class TaskWithBounds extends Task {
         final Rect mBounds;
-        final Rect mInsetBounds = new Rect();
+        final Rect mOverrideDisplayedBounds = new Rect();
         boolean mFullscreenForTest = true;
 
         TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
@@ -100,8 +100,8 @@
             outBounds.set(mBounds);
         }
         @Override
-        void getTempInsetBounds(Rect outBounds) {
-            outBounds.set(mInsetBounds);
+        Rect getOverrideDisplayedBounds() {
+            return mOverrideDisplayedBounds;
         }
         @Override
         boolean isFullscreen() {
@@ -343,14 +343,14 @@
                 taskBottom - contentInsetBottom));
 
         pf.set(0, 0, logicalWidth, logicalHeight);
-        // However if we set temp inset bounds, the insets will be computed
-        // as if our window was laid out there,  but it will be laid out according to
-        // the task bounds.
+        // If we set displayed bounds, the insets will be computed with the main task bounds
+        // but the frame will be positioned according to the displayed bounds.
         final int insetLeft = logicalWidth / 5;
         final int insetTop = logicalHeight / 5;
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
-        task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
+        task.mOverrideDisplayedBounds.set(taskBounds);
+        task.mBounds.set(insetLeft, insetTop, insetRight, insetBottom);
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw();
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -430,7 +430,6 @@
         final int taskBottom = logicalHeight / 4 * 3;
         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
         final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
-        task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
         task.mFullscreenForTest = false;
         WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
@@ -486,12 +485,12 @@
     }
 
     @Test
-    public void testDisplayCutout_tempInsetBounds() {
+    public void testDisplayCutout_tempDisplayedBounds() {
         // Regular fullscreen task and window
         final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm,
-                new Rect(0, -500, 1000, 1500));
+                new Rect(0, 0, 1000, 2000));
         task.mFullscreenForTest = false;
-        task.mInsetBounds.set(0, 0, 1000, 2000);
+        task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
         WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 4a99172..50fd188 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -30,6 +30,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityManagerInternal;
 import android.content.Context;
@@ -125,11 +126,12 @@
                 if (input != null && input.length > 1) {
                     doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
                 }
+                ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+                when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
 
                 mService = WindowManagerService.main(context, ims, false, false,
                         mPolicy = new TestWindowManagerPolicy(
-                                WindowManagerServiceRule.this::getWindowManagerService),
-                        new WindowManagerGlobalLock());
+                                WindowManagerServiceRule.this::getWindowManagerService), atms);
                 mService.mTransactionFactory = () -> {
                     final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
                     mSurfaceTransactions.add(new WeakReference<>(transaction));
@@ -173,21 +175,21 @@
         };
     }
 
-    public WindowManagerService getWindowManagerService() {
+    WindowManagerService getWindowManagerService() {
         return mService;
     }
 
-    public TestWindowManagerPolicy getWindowManagerPolicy() {
-        return mPolicy;
-    }
-
-    public void waitUntilWindowManagerHandlersIdle() {
+    void waitUntilWindowManagerHandlersIdle() {
         final WindowManagerService wm = getWindowManagerService();
-        if (wm != null) {
-            wm.mH.runWithScissors(() -> { }, 0);
-            wm.mAnimationHandler.runWithScissors(() -> { }, 0);
-            SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
+        if (wm == null) {
+            return;
         }
+        wm.mH.removeCallbacksAndMessages(null);
+        wm.mAnimationHandler.removeCallbacksAndMessages(null);
+        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
+        wm.mH.runWithScissors(() -> { }, 0);
+        wm.mAnimationHandler.runWithScissors(() -> { }, 0);
+        SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
     }
 
     private void destroyAllSurfaceTransactions() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index a1b0411..aa0ecf8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -28,6 +28,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -117,11 +120,11 @@
         // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
         // to properly set bounds values in the configuration. We must mimick those actions here.
         doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Configuration config = invocationOnMock.<Configuration>getArgument(7);
+            final Configuration config = invocationOnMock.<Configuration>getArgument(6);
             final Rect bounds = invocationOnMock.<Rect>getArgument(0);
             config.windowConfiguration.setBounds(bounds);
             return null;
-        }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
+        }).when(controller).adjustConfigurationForBounds(any(), any(), any(),
                 anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
 
         return controller;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index cef99865..d617de0 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -29,7 +29,6 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
-import java.lang.String;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
@@ -908,10 +907,16 @@
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
-            sb.append("[pa: ");
+            sb.append("[id: ");
+            sb.append(mTelecomCallId);
+            sb.append(", pa: ");
             sb.append(mAccountHandle);
             sb.append(", hdl: ");
-            sb.append(Log.pii(mHandle));
+            sb.append(Log.piiHandle(mHandle));
+            sb.append(", hdlPres: ");
+            sb.append(mHandlePresentation);
+            sb.append(", videoState: ");
+            sb.append(VideoProfile.videoStateToString(mVideoState));
             sb.append(", caps: ");
             sb.append(capabilitiesToString(mCallCapabilities));
             sb.append(", props: ");
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7db6940..6628743 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -27,8 +27,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallScreeningService;
 import com.android.internal.telecom.ICallScreeningAdapter;
+import com.android.internal.telecom.ICallScreeningService;
 
 /**
  * This service can be implemented by the default dialer (see
@@ -147,7 +147,7 @@
             private boolean mShouldSkipCallLog;
             private boolean mShouldSkipNotification;
 
-            /*
+            /**
              * Sets whether the incoming call should be blocked.
              */
             public Builder setDisallowCall(boolean shouldDisallowCall) {
@@ -155,7 +155,7 @@
                 return this;
             }
 
-            /*
+            /**
              * Sets whether the incoming call should be disconnected as if the user had manually
              * rejected it. This property should only be set to true if the call is disallowed.
              */
@@ -164,16 +164,20 @@
                 return this;
             }
 
-            /*
+            /**
              * Sets whether the incoming call should not be displayed in the call log. This property
              * should only be set to true if the call is disallowed.
+             * <p>
+             * Note: Calls will still be logged with type
+             * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
+             * is set.
              */
             public Builder setSkipCallLog(boolean shouldSkipCallLog) {
                 mShouldSkipCallLog = shouldSkipCallLog;
                 return this;
             }
 
-            /*
+            /**
              * Sets whether a missed call notification should not be shown for the incoming call.
              * This property should only be set to true if the call is disallowed.
              */
@@ -211,6 +215,17 @@
      * Called when a new incoming call is added.
      * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
      * should be called to allow or disallow the call.
+     * <p>
+     * Note: The {@link Call.Details} instance provided to a call screening service will only have
+     * the following properties set.  The rest of the {@link Call.Details} properties will be set to
+     * their default value or {@code null}.
+     * <ul>
+     *     <li>{@link Call.Details#getState()}</li>
+     *     <li>{@link Call.Details#getConnectTimeMillis()}</li>
+     *     <li>{@link Call.Details#getCreationTimeMillis()}</li>
+     *     <li>{@link Call.Details#getHandle()}</li>
+     *     <li>{@link Call.Details#getHandlePresentation()}</li>
+     * </ul>
      *
      * @param callDetails Information about a new incoming call, see {@link Call.Details}.
      */
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 054288b..2236cba 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -13,3 +13,4 @@
 shuoq@google.com
 refuhoo@google.com
 paulye@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index b3e1ffa..6a396ce 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -44,6 +45,8 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SmsApplication;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.regex.Matcher;
@@ -2962,34 +2965,36 @@
          *@hide
          */
         @SystemApi
-        public static final String MODEM_COGNITIVE = "modem_cognitive";
+        public static final String MODEM_PERSIST = "modem_cognitive";
 
         /**
-         * The max connections of this APN.
+         * The max number of connections of this APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
         @SystemApi
-        public static final String MAX_CONNS = "max_conns";
+        public static final String MAX_CONNECTIONS = "max_conns";
 
         /**
-         * The wait time for retry of the APN.
+         * The wait time for retrying the APN, in milliseconds.
          * <p>Type: INTEGER</p>
          *@hide
          */
         @SystemApi
-        public static final String WAIT_TIME = "wait_time";
+        public static final String WAIT_TIME_RETRY = "wait_time";
 
         /**
-         * The time to limit max connection for the APN.
+         * The max number of seconds this APN will support its maximum number of connections
+         * as defined in {@link #MAX_CONNECTIONS}.
          * <p>Type: INTEGER</p>
          *@hide
          */
         @SystemApi
-        public static final String MAX_CONNS_TIME = "max_conns_time";
+        public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
 
         /**
-         * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected.
+         * The MTU (maximum transmit unit) size of the mobile interface to which the APN is
+         * connected, in bytes.
          * <p>Type: INTEGER </p>
          * @hide
          */
@@ -2998,11 +3003,19 @@
 
         /**
          * APN edit status. APN could be added/edited/deleted by a user or carrier.
+         * see all possible returned APN edit status.
+         * <ul>
+         *     <li>{@link #UNEDITED}</li>
+         *     <li>{@link #USER_EDITED}</li>
+         *     <li>{@link #USER_DELETED}</li>
+         *     <li>{@link #CARRIER_EDITED}</li>
+         *     <li>{@link #CARRIER_DELETED}</li>
+         * </ul>
          * <p>Type: INTEGER </p>
          * @hide
          */
         @SystemApi
-        public static final String EDITED = "edited";
+        public static final String EDITED_STATUS = "edited";
 
         /**
          * {@code true} if this APN visible to the user, {@code false} otherwise.
@@ -3021,59 +3034,61 @@
         public static final String USER_EDITABLE = "user_editable";
 
         /**
-         * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to
-         * edit.
+         * {@link #EDITED_STATUS APN edit status} indicates that this APN has not been edited or
+         * fails to edit.
          * <p>Type: INTEGER </p>
          * @hide
          */
         @SystemApi
-        public static final int UNEDITED = 0;
+        public static final @EditStatus int UNEDITED = 0;
 
         /**
-         * {@link #EDITED APN edit status} indicates that this APN has been edited by users.
+         * {@link #EDITED_STATUS APN edit status} indicates that this APN has been edited by users.
          * <p>Type: INTEGER </p>
          * @hide
          */
         @SystemApi
-        public static final int USER_EDITED = 1;
+        public static final @EditStatus int USER_EDITED = 1;
 
         /**
-         * {@link #EDITED APN edit status} indicates that this APN has been deleted by users.
+         * {@link #EDITED_STATUS APN edit status} indicates that this APN has been deleted by users.
          * <p>Type: INTEGER </p>
          * @hide
          */
         @SystemApi
-        public static final int USER_DELETED = 2;
+        public static final @EditStatus int USER_DELETED = 2;
 
         /**
-         * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
-         * deleted by the user is still present in the new APN database and therefore must remain
-         * tagged as user deleted rather than completely removed from the database.
+         * {@link #EDITED_STATUS APN edit status} is an intermediate value used to indicate that an
+         * entry deleted by the user is still present in the new APN database and therefore must
+         * remain tagged as user deleted rather than completely removed from the database.
          * @hide
          */
         public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
 
         /**
-         * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers.
+         * {@link #EDITED_STATUS APN edit status} indicates that this APN has been edited by
+         * carriers.
          * <p>Type: INTEGER </p>
          * @hide
          */
         @SystemApi
-        public static final int CARRIER_EDITED = 4;
+        public static final @EditStatus int CARRIER_EDITED = 4;
 
         /**
-         * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers.
-         * CARRIER_DELETED values are currently not used as there is no use case. If they are used,
-         * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+         * {@link #EDITED_STATUS APN edit status} indicates that this APN has been deleted by
+         * carriers. CARRIER_DELETED values are currently not used as there is no use case.
+         * If they are used, delete() will have to change accordingly. Currently it is hardcoded to
+         * USER_DELETED.
          * <p>Type: INTEGER </p>
          * @hide
          */
-        public static final int CARRIER_DELETED = 5;
+        public static final @EditStatus int CARRIER_DELETED = 5;
 
         /**
-         * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
-         * deleted by the carrier is still present in the new APN database and therefore must remain
-         * tagged as user deleted rather than completely removed from the database.
+         * {@link #EDITED_STATUS APN edit status} is an intermediate value used to indicate that an
+         * entry deleted by the carrier is still present in the new APN database and therefore must
+         * remain tagged as user deleted rather than completely removed from the database.
          * @hide
          */
         public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
@@ -3110,14 +3125,14 @@
         public static final String APN_SET_ID = "apn_set_id";
 
         /**
-         * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a
-         * set. If the user manually selects an APN with no set set, there is no need to prioritize
-         * any specific APN set ids.
+         * Possible value for the {@link #APN_SET_ID} field. By default APNs will not belong to a
+         * set. If the user manually selects an APN without apn set id, there is no need to
+         * prioritize any specific APN set ids.
          * <p>Type: INTEGER</p>
          * @hide
          */
         @SystemApi
-        public static final int NO_SET_SET = 0;
+        public static final int NO_APN_SET_ID = 0;
 
         /**
          * A unique carrier id associated with this APN
@@ -3126,6 +3141,17 @@
          */
         public static final String CARRIER_ID = "carrier_id";
 
+        /** @hide */
+        @IntDef({
+                UNEDITED,
+                USER_EDITED,
+                USER_DELETED,
+                CARRIER_DELETED,
+                CARRIER_EDITED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface EditStatus {}
+
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 185c886..62cead1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1154,20 +1154,33 @@
      */
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
 
- /**
-  * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
-  * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
-  * the carrier
-  * CallScreeningService with the opportunity to allow or block calls.
-  * <p>
-  * The String includes the package name/the class name.
-  * Example:
-  * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
-  * <p>
-  * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
-  * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
-  * ComponentName.
-  */
+    /**
+     * String to override sim country iso.
+     * Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where
+     * a SIM can have multiple MCC from different countries.
+     * Instead, each sim carrier should have a single country code, apply per carrier based iso
+     * code as an override. The overridden value can be read from
+     * {@link TelephonyManager#getSimCountryIso()} and {@link SubscriptionInfo#getCountryIso()}
+     *
+     * @hide
+     */
+    public static final String KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING =
+            "sim_country_iso_override_string";
+
+   /**
+    * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+    * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
+    * the carrier
+    * CallScreeningService with the opportunity to allow or block calls.
+    * <p>
+    * The String includes the package name/the class name.
+    * Example:
+    * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
+    * <p>
+    * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
+    * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
+    * ComponentName.
+    */
     public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
 
     /**
@@ -2533,6 +2546,7 @@
         sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+        sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
         sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
         sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
index b6e6cba..5d809d0 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -33,17 +33,31 @@
      */
     public final boolean isNrAvailable;
 
+    /**
+     * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving
+     * cell.
+     *
+     * True the primary serving cell is LTE cell and the plmn-InfoList-r15 is present in SIB2 and
+     * at least one bit in this list is true, otherwise this value should be false.
+     *
+     * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+     */
+    public final boolean isEnDcAvailable;
+
     DataSpecificRegistrationStates(
-            int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable) {
+            int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
+            boolean isEnDcAvailable) {
         this.maxDataCalls = maxDataCalls;
         this.isDcNrRestricted = isDcNrRestricted;
         this.isNrAvailable = isNrAvailable;
+        this.isEnDcAvailable = isEnDcAvailable;
     }
 
     private DataSpecificRegistrationStates(Parcel source) {
         maxDataCalls = source.readInt();
         isDcNrRestricted = source.readBoolean();
         isNrAvailable = source.readBoolean();
+        isEnDcAvailable = source.readBoolean();
     }
 
     @Override
@@ -51,6 +65,7 @@
         dest.writeInt(maxDataCalls);
         dest.writeBoolean(isDcNrRestricted);
         dest.writeBoolean(isNrAvailable);
+        dest.writeBoolean(isEnDcAvailable);
     }
 
     @Override
@@ -65,13 +80,14 @@
                 .append(" maxDataCalls = " + maxDataCalls)
                 .append(" isDcNrRestricted = " + isDcNrRestricted)
                 .append(" isNrAvailable = " + isNrAvailable)
+                .append(" isEnDcAvailable = " + isEnDcAvailable)
                 .append(" }")
                 .toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable);
+        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable);
     }
 
     @Override
@@ -83,7 +99,8 @@
         DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
         return this.maxDataCalls == other.maxDataCalls
                 && this.isDcNrRestricted == other.isDcNrRestricted
-                && this.isNrAvailable == other.isNrAvailable;
+                && this.isNrAvailable == other.isNrAvailable
+                && this.isEnDcAvailable == other.isEnDcAvailable;
     }
 
     public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index aee744f..b00665e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -219,12 +219,13 @@
     public NetworkRegistrationState(int domain, int transportType, int regState,
             int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
             int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls,
-            boolean isDcNrRestricted, boolean isNrAvailable) {
+            boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable) {
         this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
                 availableServices, cellIdentity);
 
         mDataSpecificStates = new DataSpecificRegistrationStates(
-                maxDataCalls, isDcNrRestricted, isNrAvailable);
+                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable);
+        updateNrStatus(mDataSpecificStates);
     }
 
     protected NetworkRegistrationState(Parcel source) {
@@ -448,6 +449,34 @@
         dest.writeInt(mNrStatus);
     }
 
+    /**
+     * Use the 5G NR Non-Standalone indicators from the network registration state to update the
+     * NR status. There are 3 indicators in the network registration state:
+     *
+     * 1. if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving cell.
+     * 2. if NR is supported by the selected PLMN.
+     * 3. if the use of dual connectivity with NR is restricted.
+     *
+     * The network has 5G NR capability if E-UTRA-NR Dual Connectivity is supported by the primary
+     * serving cell.
+     *
+     * The use of NR 5G is not restricted If the network has 5G NR capability and both the use of
+     * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
+     * NR is restricted.
+     *
+     * @param state data specific registration state contains the 5G NR indicators.
+     */
+    private void updateNrStatus(DataSpecificRegistrationStates state) {
+        mNrStatus = NR_STATUS_NONE;
+        if (state.isEnDcAvailable) {
+            if (!state.isDcNrRestricted && state.isNrAvailable) {
+                mNrStatus = NR_STATUS_NOT_RESTRICTED;
+            } else {
+                mNrStatus = NR_STATUS_RESTRICTED;
+            }
+        }
+    }
+
     public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
             new Parcelable.Creator<NetworkRegistrationState>() {
         @Override
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 13fbeaa..ca0c854 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -118,6 +118,13 @@
      */
     public static final int FREQUENCY_RANGE_MMWAVE = 4;
 
+    private static final List<Integer> FREQUENCY_RANGE_ORDER = Arrays.asList(
+            FREQUENCY_RANGE_UNKNOWN,
+            FREQUENCY_RANGE_LOW,
+            FREQUENCY_RANGE_MID,
+            FREQUENCY_RANGE_HIGH,
+            FREQUENCY_RANGE_MMWAVE);
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "DUPLEX_MODE_",
@@ -1835,4 +1842,13 @@
             mNetworkRegistrationStates.add(regState);
         }
     }
+
+    /**
+     * @hide
+     */
+    public static final int getBetterNRFrequencyRange(int range1, int range2) {
+        return FREQUENCY_RANGE_ORDER.indexOf(range1) > FREQUENCY_RANGE_ORDER.indexOf(range2)
+                ? range1
+                : range2;
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4b24646..2c06c47 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -407,6 +407,13 @@
     public static final String MNC = "mnc";
 
     /**
+     * TelephonyProvider column name for the iso country code associated with a SIM.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String ISO_COUNTRY_CODE = "iso_country_code";
+
+    /**
      * TelephonyProvider column name for the sim provisioning status associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
@@ -1583,14 +1590,23 @@
         return subId;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public void setDefaultSmsSubId(int subId) {
-        if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
+    /**
+     * Set the subscription which will be used by default for SMS, with the subscription which
+     * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+     * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+     *
+     * @param subscriptionId the supplied subscription ID
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDefaultSmsSubId(int subscriptionId) {
+        if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                iSub.setDefaultSmsSubId(subId);
+                iSub.setDefaultSmsSubId(subscriptionId);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1638,14 +1654,23 @@
         return subId;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public void setDefaultDataSubId(int subId) {
-        if (VDBG) logd("setDataSubscription sub id = " + subId);
+    /**
+     * Set the subscription which will be used by default for data, with the subscription which
+     * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+     * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+     *
+     * @param subscriptionId the supplied subscription ID
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDefaultDataSubId(int subscriptionId) {
+        if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                iSub.setDefaultDataSubId(subId);
+                iSub.setDefaultDataSubId(subscriptionId);
             }
         } catch (RemoteException ex) {
             // ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 45d914e..fa9b76d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5541,19 +5541,40 @@
     public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull NumberVerificationCallback callback) {
+        if (executor == null) {
+            throw new NullPointerException("Executor must be non-null");
+        }
+        if (callback == null) {
+            throw new NullPointerException("Callback must be non-null");
+        }
+
         INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
             @Override
-            public void onCallReceived(String phoneNumber) throws RemoteException {
-                Binder.withCleanCallingIdentity(() -> callback.onCallReceived(phoneNumber));
+            public void onCallReceived(String phoneNumber) {
+                Binder.withCleanCallingIdentity(() ->
+                        executor.execute(() ->
+                                callback.onCallReceived(phoneNumber)));
             }
 
             @Override
-            public void onVerificationFailed(int reason) throws RemoteException {
-                Binder.withCleanCallingIdentity(() -> callback.onVerificationFailed(reason));
+            public void onVerificationFailed(int reason) {
+                Binder.withCleanCallingIdentity(() ->
+                        executor.execute(() ->
+                                callback.onVerificationFailed(reason)));
             }
         };
 
-        // TODO -- call the aidl method
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
+                        getOpPackageName());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "requestNumberVerification RemoteException", ex);
+            executor.execute(() ->
+                    callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED));
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 4fd7066..a1afc08 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -693,7 +693,7 @@
             mmsc, mmsProxyAddress, mmsProxyPort, user, password, authType, mApnTypeBitmask,
             protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, profileId,
             modemCognitive, maxConns, waitTime, maxConnsTime, mtu, mvnoType, mvnoMatchData,
-            Carriers.NO_SET_SET, TelephonyManager.UNKNOWN_CARRIER_ID);
+            Carriers.NO_APN_SET_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
     }
 
     /**
@@ -740,11 +740,11 @@
             networkTypeBitmask,
             cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
             cursor.getInt(cursor.getColumnIndexOrThrow(
-                Telephony.Carriers.MODEM_COGNITIVE)) == 1,
-            cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
-            cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME)),
+                Telephony.Carriers.MODEM_PERSIST)) == 1,
+            cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNECTIONS)),
+            cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME_RETRY)),
             cursor.getInt(cursor.getColumnIndexOrThrow(
-                Telephony.Carriers.MAX_CONNS_TIME)),
+                Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS)),
             cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
             getMvnoTypeIntFromString(
                 cursor.getString(cursor.getColumnIndexOrThrow(
@@ -870,7 +870,7 @@
         int mtu = UNSET_MTU;
         String mvnoType = "";
         String mvnoMatchData = "";
-        int apnSetId = Carriers.NO_SET_SET;
+        int apnSetId = Carriers.NO_APN_SET_ID;
         int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
         if (version == 1) {
             typeArray = new String[a.length - 13];
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 3b1ef3f..994c49c 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -155,7 +155,7 @@
      * Requests all the profiles on eUicc.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and all the profiles.
      */
     public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor,
@@ -179,7 +179,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and profile.
      */
     public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
@@ -204,7 +204,7 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
      * @param refresh Whether sending the REFRESH command to modem.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
      */
     public void disableProfile(String cardId, String iccid, boolean refresh,
@@ -230,7 +230,7 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile to switch to.
      * @param refresh Whether sending the REFRESH command to modem.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
      */
     public void switchToProfile(String cardId, String iccid, boolean refresh,
@@ -255,7 +255,7 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
      * @param nickname The nickname of the profile.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
      */
     public void setNickname(String cardId, String iccid, String nickname,
@@ -279,7 +279,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
      */
     public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
@@ -304,7 +304,7 @@
      * @param cardId The Id of the eUICC.
      * @param options Bits of the options of resetting which parts of the eUICC memory. See
      *     EuiccCard for details.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
      */
     public void resetMemory(String cardId, @ResetOption int options,
@@ -327,7 +327,7 @@
      * Requests the default SM-DP+ address from eUICC.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the default SM-DP+ address.
      */
     public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor,
@@ -350,7 +350,7 @@
      * Requests the SM-DS address from eUICC.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the SM-DS address.
      */
     public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor,
@@ -374,7 +374,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param defaultSmdpAddress The default SM-DP+ address to set.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
      */
     public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress,
@@ -398,7 +398,7 @@
      * Requests Rules Authorisation Table.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the rule authorisation table.
      */
     public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor,
@@ -421,7 +421,7 @@
      * Requests the eUICC challenge for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the challenge.
      */
     public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor,
@@ -444,7 +444,7 @@
      * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the info1.
      */
     public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor,
@@ -467,7 +467,7 @@
      * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the info2.
      */
     public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor,
@@ -500,7 +500,7 @@
      *     GSMA RSP v2.0+.
      * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
      *     SM-DP+ server.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
      */
@@ -540,7 +540,7 @@
      *     SM-DP+ server.
      * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
      *     by SM-DP+ server.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
      */
@@ -572,7 +572,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
      */
@@ -601,7 +601,7 @@
      * @param cardId The Id of the eUICC.
      * @param transactionId the transaction ID returned by SM-DP+ server.
      * @param reason the cancel reason.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and an byte[] which represents a
      *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
      */
@@ -630,7 +630,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void listNotifications(String cardId, @EuiccNotification.Event int events,
@@ -654,7 +654,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
@@ -678,7 +678,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param seqNumber the sequence number of the notification.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code and the notification.
      */
     public void retrieveNotification(String cardId, int seqNumber,
@@ -702,7 +702,7 @@
      *
      * @param cardId The Id of the eUICC.
      * @param seqNumber the sequence number of the notification.
-     * @param executor The executor through which the callback should be invoke.
+     * @param executor The executor through which the callback should be invoked.
      * @param callback the callback to get the result code.
      */
     public void removeNotificationFromList(String cardId, int seqNumber,
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 32e939a0..399dc52 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -35,6 +35,7 @@
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.PhoneNumberRange;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -49,6 +50,7 @@
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.OperatorInfo;
 
 import java.util.List;
@@ -871,6 +873,17 @@
     String getCdmaMin(int subId);
 
     /**
+     * Request that the next incoming call from a number matching {@code range} be intercepted.
+     * @param range The range of phone numbers the caller expects a phone call from.
+     * @param timeoutMillis The amount of time to wait for such a call, or
+     *                      {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+     * @param callback the callback aidl
+     * @param callingPackage the calling package name.
+     */
+    void requestNumberVerification(in PhoneNumberRange range, long timeoutMillis,
+            in INumberVerificationCallback callback, String callingPackage);
+
+    /**
      * Has the calling application been granted special privileges by the carrier.
      *
      * If any of the packages in the calling UID has carrier privileges, the
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 17bcea0..71529fd 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -96,6 +96,7 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.INetd;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
@@ -228,6 +229,7 @@
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
     @Mock INetworkPolicyManager mNpm;
+    @Mock INetd mMockNetd;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -926,8 +928,9 @@
 
         public WrappedConnectivityService(Context context, INetworkManagementService netManager,
                 INetworkStatsService statsService, INetworkPolicyManager policyManager,
-                IpConnectivityLog log) {
+                IpConnectivityLog log, INetd netd) {
             super(context, netManager, statsService, policyManager, log);
+            mNetd = netd;
             mLingerDelayMs = TEST_LINGER_DELAY_MS;
         }
 
@@ -1087,7 +1090,8 @@
                 mNetworkManagementService,
                 mStatsService,
                 mNpm,
-                mock(IpConnectivityLog.class));
+                mock(IpConnectivityLog.class),
+                mMockNetd);
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
                 ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -4766,4 +4770,30 @@
         // Clean up
         mCm.unregisterNetworkCallback(networkCallback);
     }
+
+    private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6";
+
+    private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception {
+        String[] values = tcpBufferSizes.split(",");
+        String rmemValues = String.join(" ", values[0], values[1], values[2]);
+        String wmemValues = String.join(" ", values[3], values[4], values[5]);
+        waitForIdle();
+        verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
+        reset(mMockNetd);
+    }
+
+    @Test
+    public void testTcpBufferReset() throws Exception {
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        reset(mMockNetd);
+        // Simple connection should have updated tcp buffer size.
+        mCellNetworkAgent.connect(false);
+        verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
+
+        // Change link Properties should have updated tcp buffer size.
+        LinkProperties lp = new LinkProperties();
+        lp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4dc0341..d8f9618 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -542,7 +542,7 @@
 
         verify(mMockNetd)
                 .ipSecApplyTransportModeTransform(
-                        eq(pfd.getFileDescriptor()),
+                        eq(pfd),
                         eq(mUid),
                         eq(IpSecManager.DIRECTION_OUT),
                         anyString(),
@@ -555,7 +555,7 @@
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransforms(pfd);
 
-        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
     }
 
     private IpSecTunnelInterfaceResponse createAndValidateTunnel(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 2c94a60..724446e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -425,7 +425,7 @@
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransforms(pfd);
 
-        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
     }
 
     @Test
@@ -620,10 +620,10 @@
                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
 
         FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
-        ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+        ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
                     try {
                         StructStat sockStat = Os.fstat(sockFd);
-                        StructStat argStat = Os.fstat(arg);
+                        StructStat argStat = Os.fstat(arg.getFileDescriptor());
 
                         return sockStat.st_ino == argStat.st_ino
                                 && sockStat.st_dev == argStat.st_dev;
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index ed70fb3..df0daeb 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -240,6 +240,12 @@
         }
         break;
 
+      case android::RES_TABLE_OVERLAYABLE_TYPE:
+        if (!ParseOverlayable(parser.chunk())) {
+          return false;
+        }
+        break;
+
       default:
         diag_->Warn(DiagMessage(source_)
                     << "unexpected chunk type "
@@ -383,24 +389,12 @@
       return false;
     }
 
-    const uint32_t type_spec_flags = entry_type_spec_flags_[res_id];
-    if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0 ||
-        (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) != 0) {
-      if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
-        Visibility visibility;
-        visibility.level = Visibility::Level::kPublic;
-        visibility.source = source_.WithLine(0);
-        if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
-          return false;
-        }
-      }
-
-      if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
-        Overlayable overlayable;
-        overlayable.source = source_.WithLine(0);
-        if (!table_->AddOverlayableMangled(name, overlayable, diag_)) {
-          return false;
-        }
+    if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
+      Visibility visibility;
+      visibility.level = Visibility::Level::kPublic;
+      visibility.source = source_.WithLine(0);
+      if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
+        return false;
       }
 
       // Erase the ID from the map once processed, so that we don't mark the same symbol more than
@@ -433,6 +427,72 @@
   return true;
 }
 
+bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
+  const ResTable_overlayable_header* header = ConvertTo<ResTable_overlayable_header>(chunk);
+  if (!header) {
+    diag_->Error(DiagMessage(source_) << "corrupt ResTable_category_header chunk");
+    return false;
+  }
+
+  ResChunkPullParser parser(GetChunkData(chunk),
+                            GetChunkDataLen(chunk));
+  while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+    if (util::DeviceToHost16(parser.chunk()->type) == android::RES_TABLE_OVERLAYABLE_POLICY_TYPE) {
+      const ResTable_overlayable_policy_header* policy_header =
+          ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
+
+      std::vector<Overlayable::Policy> policies;
+      if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
+        policies.push_back(Overlayable::Policy::kPublic);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
+        policies.push_back(Overlayable::Policy::kSystem);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
+        policies.push_back(Overlayable::Policy::kVendor);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
+        policies.push_back(Overlayable::Policy::kProduct);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
+        policies.push_back(Overlayable::Policy::kProductServices);
+      }
+
+      const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
+          ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
+      const ResTable_ref* const ref_end = ref_begin
+          + util::DeviceToHost32(policy_header->entry_count);
+      for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) {
+        ResourceId res_id(util::DeviceToHost32(ref_iter->ident));
+        const auto iter = id_index_.find(res_id);
+
+        // If the overlayable chunk comes before the type chunks, the resource ids and resource name
+        // pairing will not exist at this point.
+        if (iter == id_index_.cend()) {
+          diag_->Error(DiagMessage(source_) << "failed to find resource name for overlayable"
+                                            << " resource " << res_id);
+          return false;
+        }
+
+        for (Overlayable::Policy policy : policies) {
+          Overlayable overlayable;
+          overlayable.source = source_.WithLine(0);
+          overlayable.policy = policy;
+          if (!table_->AddOverlayable(iter->second, overlayable, diag_)) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
 std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
                                                        const ConfigDescription& config,
                                                        const android::Res_value& value) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 2bdc051..a2eee50 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -54,6 +54,7 @@
   bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
   bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
   bool ParseLibrary(const android::ResChunk_header* chunk);
+  bool ParseOverlayable(const android::ResChunk_header* chunk);
 
   std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
                                    const android::ConfigDescription& config,
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 6c1a9ba..976c328 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -24,6 +24,7 @@
 #include "android-base/logging.h"
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/ResourceUtils.h"
 
 #include "ResourceTable.h"
 #include "ResourceValues.h"
@@ -216,6 +217,11 @@
   size_t entry_count_ = 0;
 };
 
+struct PolicyChunk {
+  uint32_t policy_flags;
+  std::set<ResourceId> ids;
+};
+
 class PackageFlattener {
  public:
   PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
@@ -267,6 +273,8 @@
       FlattenLibrarySpec(buffer);
     }
 
+    FlattenOverlayable(buffer);
+
     pkg_writer.Finish();
     return true;
   }
@@ -413,6 +421,97 @@
     return sorted_entries;
   }
 
+  void FlattenOverlayable(BigBuffer* buffer) {
+    std::vector<PolicyChunk> policies;
+
+    CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
+    for (auto& type : package_->types) {
+      CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
+      for (auto& entry : type->entries) {
+        CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
+
+        // TODO(b/120298168): Convert the policies vector to a policy set or bitmask
+        if (!entry->overlayable_declarations.empty()) {
+          uint16_t policy_flags = 0;
+          for (Overlayable overlayable : entry->overlayable_declarations) {
+            if (overlayable.policy) {
+              switch (overlayable.policy.value()) {
+                case Overlayable::Policy::kPublic:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+                  break;
+                case Overlayable::Policy::kSystem:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+                  break;
+                case Overlayable::Policy::kVendor:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+                  break;
+                case Overlayable::Policy::kProduct:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+                  break;
+                case Overlayable::Policy::kProductServices:
+                  policy_flags |=
+                      ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+                  break;
+              }
+            } else {
+              // Encode overlayable entries defined without a policy as publicly overlayable
+              policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+            }
+          }
+
+          // Find the overlayable policy chunk with the same policies as the entry
+          PolicyChunk* policy_chunk = nullptr;
+          for (PolicyChunk& policy : policies) {
+            if (policy.policy_flags == policy_flags) {
+              policy_chunk = &policy;
+              break;
+            }
+          }
+
+          // Create a new policy chunk if an existing one with the same policy cannot be found
+          if (policy_chunk == nullptr) {
+            PolicyChunk p;
+            p.policy_flags = policy_flags;
+            policies.push_back(p);
+            policy_chunk = &policies.back();
+          }
+
+          policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
+                                                       entry->id.value()));
+        }
+      }
+    }
+
+    if (policies.empty()) {
+      // Only write the overlayable chunk if the APK has overlayable entries
+      return;
+    }
+
+    ChunkWriter writer(buffer);
+    writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
+
+    // Write each policy block for the overlayable
+    for (PolicyChunk& policy : policies) {
+      ChunkWriter policy_writer(buffer);
+      ResTable_overlayable_policy_header* policy_type =
+          policy_writer.StartChunk<ResTable_overlayable_policy_header>(
+              RES_TABLE_OVERLAYABLE_POLICY_TYPE);
+      policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
+      policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
+
+      // Write the ids after the policy header
+      ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
+      for (const ResourceId& id : policy.ids) {
+        id_block->ident = util::HostToDevice32(id.id);
+        id_block++;
+      }
+
+      policy_writer.Finish();
+    }
+
+    writer.Finish();
+  }
+
   bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
                        BigBuffer* buffer) {
     ChunkWriter type_spec_writer(buffer);
@@ -446,11 +545,6 @@
         config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
       }
 
-      if (!entry->overlayable_declarations.empty()) {
-        config_masks[entry->id.value()] |=
-            util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
-      }
-
       const size_t config_count = entry->values.size();
       for (size_t i = 0; i < config_count; i++) {
         const ConfigDescription& config = entry->values[i]->config;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index cd1414c..410efbe 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,24 +628,108 @@
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayable) {
+  std::string name = "com.app.test:integer/overlayable";
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
-          .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
+          .AddSimple(name, ResourceId(0x7f020000))
+          .AddOverlayable(name, Overlayable::Policy::kProduct)
+          .AddOverlayable(name, Overlayable::Policy::kSystem)
+          .AddOverlayable(name, Overlayable::Policy::kVendor)
           .Build();
 
-  ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
-                                    Overlayable{}, test::GetDiagnostics()));
+  ResourceTable output_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
 
-  ResTable res_table;
-  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
-
-  const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable");
-  uint32_t spec_flags = 0u;
-  ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr,
-                                          0u, nullptr, 0u, &spec_flags),
-              Gt(0u));
-  EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE);
+  auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kSystem);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kVendor);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+            Overlayable::Policy::kProduct);
 }
 
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  std::string name_one = "com.app.test:integer/overlayable_one";
+  std::string name_two = "com.app.test:integer/overlayable_two";
+  std::string name_three = "com.app.test:integer/overlayable_three";
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .AddOverlayable(name_zero, Overlayable::Policy::kProduct)
+          .AddOverlayable(name_zero, Overlayable::Policy::kSystem)
+          .AddOverlayable(name_zero, Overlayable::Policy::kProductServices)
+          .AddSimple(name_one, ResourceId(0x7f020001))
+          .AddOverlayable(name_one, Overlayable::Policy::kPublic)
+          .AddOverlayable(name_one, Overlayable::Policy::kSystem)
+          .AddSimple(name_two, ResourceId(0x7f020002))
+          .AddOverlayable(name_two, Overlayable::Policy::kProduct)
+          .AddOverlayable(name_two, Overlayable::Policy::kSystem)
+          .AddOverlayable(name_two, Overlayable::Policy::kProductServices)
+          .AddSimple(name_three, ResourceId(0x7f020003))
+          .AddOverlayable(name_three, {})
+          .Build();
+
+  ResourceTable output_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
+
+  auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kSystem);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kProduct);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+            Overlayable::Policy::kProductServices);
+
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 2);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kPublic);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kSystem);
+
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kSystem);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kProduct);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+            Overlayable::Policy::kProductServices);
+
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 1);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kPublic);
+
+}
+
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 3a5d585..1b6626a 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -368,7 +368,16 @@
         // Symbol state information may be lost if there is no value for the resource.
         if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
           context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
-                                           << "no definition for declared symbol '" << name << "'");
+                                               << "no definition for declared symbol '" << name
+                                               << "'");
+          error = true;
+        }
+
+        // Ensure that definitions for values declared as overlayable exist
+        if (!entry->overlayable_declarations.empty() && entry->values.empty()) {
+          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_declarations[0].source)
+                                           << "no definition for overlayable symbol '"
+                                           << name << "'");
           error = true;
         }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8e0d4ac..cad6d29 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -156,33 +156,39 @@
     public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1;
 
     /**
+     * Reason code if the user has disallowed "android:change_wifi_state" app-ops from the app.
+     * @see android.app.AppOpsManager#unsafeCheckOp(String, int, String).
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2;
+
+    /**
      * Reason code if one or more of the network suggestions added already exists in platform's
      * database.
      * @see WifiNetworkSuggestion#equals(Object)
      */
-    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2;
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3;
 
     /**
      * Reason code if the number of network suggestions provided by the app crosses the max
      * threshold set per app.
      * @see #getMaxNumberOfNetworkSuggestionsPerApp()
      */
-    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3;
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4;
 
     /**
      * Reason code if one or more of the network suggestions removed does not exist in platform's
      * database.
      */
-    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4;
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5;
 
     @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
             STATUS_NETWORK_SUGGESTIONS_SUCCESS,
             STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
+            STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED,
             STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
             STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
             STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
     })
-
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkSuggestionsStatusCode {}
 
@@ -1982,6 +1988,8 @@
     public static final int WIFI_FEATURE_WPA3_SUITE_B     = 0x10000000; // WPA3-Enterprise Suite-B
     /** @hide */
     public static final int WIFI_FEATURE_OWE              = 0x20000000; // Enhanced Open
+    /** @hide */
+    public static final int WIFI_FEATURE_LOW_LATENCY      = 0x40000000; // Low Latency modes
 
     private int getSupportedFeatures() {
         try {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index fc5caf0..acc0518 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -357,11 +357,12 @@
          */
         private int mBucketsScanned;
         /**
-         * Indicates that the scan results received are as a result of a scan of all available
-         * channels. This should only be expected to function for single scans.
+         * Bands scanned. One of the WIFI_BAND values.
+         * Will be {@link #WIFI_BAND_UNSPECIFIED} if the list of channels do not fully cover
+         * any of the bands.
          * {@hide}
          */
-        private boolean mAllChannelsScanned;
+        private int mBandScanned;
         /** all scan results discovered in this scan, sorted by timestamp in ascending order */
         private ScanResult mResults[];
 
@@ -374,12 +375,12 @@
         }
 
         /** {@hide} */
-        public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned,
-                ScanResult[] results) {
+        public ScanData(int id, int flags, int bucketsScanned, int bandScanned,
+                        ScanResult[] results) {
             mId = id;
             mFlags = flags;
             mBucketsScanned = bucketsScanned;
-            mAllChannelsScanned = allChannelsScanned;
+            mBandScanned = bandScanned;
             mResults = results;
         }
 
@@ -387,7 +388,7 @@
             mId = s.mId;
             mFlags = s.mFlags;
             mBucketsScanned = s.mBucketsScanned;
-            mAllChannelsScanned = s.mAllChannelsScanned;
+            mBandScanned = s.mBandScanned;
             mResults = new ScanResult[s.mResults.length];
             for (int i = 0; i < s.mResults.length; i++) {
                 ScanResult result = s.mResults[i];
@@ -410,8 +411,8 @@
         }
 
         /** {@hide} */
-        public boolean isAllChannelsScanned() {
-            return mAllChannelsScanned;
+        public int getBandScanned() {
+            return mBandScanned;
         }
 
         public ScanResult[] getResults() {
@@ -429,7 +430,7 @@
                 dest.writeInt(mId);
                 dest.writeInt(mFlags);
                 dest.writeInt(mBucketsScanned);
-                dest.writeInt(mAllChannelsScanned ? 1 : 0);
+                dest.writeInt(mBandScanned);
                 dest.writeInt(mResults.length);
                 for (int i = 0; i < mResults.length; i++) {
                     ScanResult result = mResults[i];
@@ -447,13 +448,13 @@
                         int id = in.readInt();
                         int flags = in.readInt();
                         int bucketsScanned = in.readInt();
-                        boolean allChannelsScanned = in.readInt() != 0;
+                        int bandScanned = in.readInt();
                         int n = in.readInt();
                         ScanResult results[] = new ScanResult[n];
                         for (int i = 0; i < n; i++) {
                             results[i] = ScanResult.CREATOR.createFromParcel(in);
                         }
-                        return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results);
+                        return new ScanData(id, flags, bucketsScanned, bandScanned, results);
                     }
 
                     public ScanData[] newArray(int size) {
@@ -759,6 +760,7 @@
      *                 Multiple requests should also not share this object.
      * {@hide}
      */
+    @RequiresPermission(Manifest.permission.NETWORK_STACK)
     public void registerScanListener(ScanListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
         int key = addListener(listener);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index b0ed110..e0442f2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -22,7 +22,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -1695,7 +1694,6 @@
      * @param listener for callback on success or failure. Can be null.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void factoryReset(@NonNull Channel c, @Nullable ActionListener listener) {
         checkChannel(c);
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index da42dcf..cf1ed8f 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -26,6 +27,7 @@
 import android.content.Context;
 import android.net.wifi.WifiScanner.PnoSettings;
 import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork;
+import android.net.wifi.WifiScanner.ScanData;
 import android.net.wifi.WifiScanner.ScanSettings;
 import android.os.Handler;
 import android.os.Parcel;
@@ -203,4 +205,29 @@
         assertNotNull(pnoNetwork.frequencies);
     }
 
+    /**
+     * Verify parcel read/write for ScanData.
+     */
+    @Test
+    public void verifyScanDataParcel() throws Exception {
+        ScanData writeScanData = new ScanData(2, 0, 3,
+                WifiScanner.WIFI_BAND_BOTH_WITH_DFS, new ScanResult[0]);
+
+        ScanData readScanData = parcelWriteRead(writeScanData);
+        assertEquals(writeScanData.getId(), readScanData.getId());
+        assertEquals(writeScanData.getFlags(), readScanData.getFlags());
+        assertEquals(writeScanData.getBucketsScanned(), readScanData.getBucketsScanned());
+        assertEquals(writeScanData.getBandScanned(), readScanData.getBandScanned());
+        assertArrayEquals(writeScanData.getResults(), readScanData.getResults());
+    }
+
+    /**
+     * Write the provided {@link ScanData} to a parcel and deserialize it.
+     */
+    private static ScanData parcelWriteRead(ScanData writeScanData) throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeScanData.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        return ScanData.CREATOR.createFromParcel(parcel);
+    }
 }