Merge "DO NOT MERGE Fix French locale % escaping" into pi-dev
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
index 55b97e7..dc34b7f 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -117,6 +117,26 @@
     }
 
     @Test
+    public void testNewLayout_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TextView textView = new TextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
+                TEXT_WIDTH, false);
+        }
+    }
+
+    @Test
     public void testNewLayout_PrecomputedText() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         BoringLayout.Metrics metrics = new BoringLayout.Metrics();
@@ -179,6 +199,24 @@
     }
 
     @Test
+    public void testSetText_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TextView textView = new TextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.setText(text);
+        }
+    }
+
+    @Test
     public void testSetText_PrecomputedText() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         BoringLayout.Metrics metrics = new BoringLayout.Metrics();
@@ -222,8 +260,8 @@
     @Test
     public void testOnMeasure_RandomText() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
-        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
@@ -240,10 +278,31 @@
     }
 
     @Test
+    public void testOnMeasure_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onMeasure(width, height);
+        }
+    }
+
+    @Test
     public void testOnMeasure_PrecomputedText() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
-        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
         while (state.keepRunning()) {
             state.pauseTiming();
             final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
@@ -265,8 +324,8 @@
     @Test
     public void testOnMeasure_PrecomputedText_Selectable() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
-        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
         while (state.keepRunning()) {
             state.pauseTiming();
             final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
@@ -289,8 +348,8 @@
     @Test
     public void testOnDraw_RandomText() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
-        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
@@ -299,8 +358,34 @@
             textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
             textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
             textView.setText(text);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            final DisplayListCanvas c = node.start(
+                textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
-            textView.onMeasure(width, height);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onDraw(c);
+        }
+    }
+
+    @Test
+    public void testOnDraw_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
             final DisplayListCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
@@ -314,8 +399,8 @@
     @Test
     public void testOnDraw_PrecomputedText() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
-        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
@@ -327,8 +412,8 @@
             final TestableTextView textView = new TestableTextView(getContext());
             textView.setTextMetricsParams(params);
             textView.setText(text);
-            textView.nullLayouts();
-            textView.onMeasure(width, height);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
             final DisplayListCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
@@ -356,8 +441,8 @@
             textView.setTextIsSelectable(true);
             textView.setTextMetricsParams(params);
             textView.setText(text);
-            textView.nullLayouts();
-            textView.onMeasure(width, height);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
             final DisplayListCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
diff --git a/api/current.txt b/api/current.txt
index 7672fb0..b3be727 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7278,7 +7278,6 @@
   public abstract class SliceProvider extends android.content.ContentProvider {
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
-    method public final java.lang.String getBindingPackage();
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
@@ -14437,6 +14436,7 @@
     method public static android.graphics.Typeface createFromFile(java.lang.String);
     method public static android.graphics.Typeface defaultFromStyle(int);
     method public int getStyle();
+    method public int getWeight();
     method public final boolean isBold();
     method public final boolean isItalic();
     field public static final int BOLD = 1; // 0x1
@@ -16435,8 +16435,8 @@
   }
 
   public final class SessionConfiguration {
-    ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback);
-    method public java.util.concurrent.Executor getExecutor();
+    ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler);
+    method public android.os.Handler getHandler();
     method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
     method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
     method public android.hardware.camera2.CaptureRequest getSessionParameters();
@@ -24052,13 +24052,13 @@
     ctor public MediaMetadataRetriever();
     method public java.lang.String extractMetadata(int);
     method public byte[] getEmbeddedPicture();
-    method public android.graphics.Bitmap getFrameAtIndex(int);
+    method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
     method public android.graphics.Bitmap getFrameAtTime(long, int);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
-    method public android.graphics.Bitmap[] getFramesAtIndex(int, int);
-    method public android.graphics.Bitmap getImageAtIndex(int);
-    method public android.graphics.Bitmap getPrimaryImage();
+    method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
+    method public android.graphics.Bitmap getPrimaryImage(android.media.MediaMetadataRetriever.BitmapParams);
     method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
     method public void release();
     method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
@@ -24104,6 +24104,13 @@
     field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public static final class MediaMetadataRetriever.BitmapParams {
+    ctor public MediaMetadataRetriever.BitmapParams();
+    method public android.graphics.Bitmap.Config getActualConfig();
+    method public android.graphics.Bitmap.Config getPreferredConfig();
+    method public void setPreferredConfig(android.graphics.Bitmap.Config);
+  }
+
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
     ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -24351,7 +24358,6 @@
     method public abstract android.media.MediaDrm.KeyRequest getDrmKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
     method public abstract java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer2.NoDrmSchemeException;
     method public abstract long getDuration();
-    method public abstract int getMediaPlayer2State();
     method public abstract android.os.PersistableBundle getMetrics();
     method public abstract android.media.PlaybackParams getPlaybackParams();
     method public abstract int getSelectedTrack(int);
@@ -24377,33 +24383,35 @@
     method public abstract void setPlaybackParams(android.media.PlaybackParams);
     method public abstract void setSurface(android.view.Surface);
     method public abstract void setSyncParams(android.media.SyncParams);
-    field public static final int MEDIAPLAYER2_STATE_ERROR = 5; // 0x5
-    field public static final int MEDIAPLAYER2_STATE_IDLE = 1; // 0x1
-    field public static final int MEDIAPLAYER2_STATE_PAUSED = 3; // 0x3
-    field public static final int MEDIAPLAYER2_STATE_PLAYING = 4; // 0x4
-    field public static final int MEDIAPLAYER2_STATE_PREPARED = 2; // 0x2
-    field public static final int MEDIA_CALL_ATTACH_AUX_EFFECT = 1; // 0x1
-    field public static final int MEDIA_CALL_DESELECT_TRACK = 2; // 0x2
-    field public static final int MEDIA_CALL_LOOP_CURRENT = 3; // 0x3
-    field public static final int MEDIA_CALL_PAUSE = 4; // 0x4
-    field public static final int MEDIA_CALL_PLAY = 5; // 0x5
-    field public static final int MEDIA_CALL_PREPARE = 6; // 0x6
-    field public static final int MEDIA_CALL_RELEASE_DRM = 12; // 0xc
-    field public static final int MEDIA_CALL_RESTORE_DRM_KEYS = 13; // 0xd
-    field public static final int MEDIA_CALL_SEEK_TO = 14; // 0xe
-    field public static final int MEDIA_CALL_SELECT_TRACK = 15; // 0xf
-    field public static final int MEDIA_CALL_SET_AUDIO_ATTRIBUTES = 16; // 0x10
-    field public static final int MEDIA_CALL_SET_AUDIO_SESSION_ID = 17; // 0x11
-    field public static final int MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL = 18; // 0x12
-    field public static final int MEDIA_CALL_SET_DATA_SOURCE = 19; // 0x13
-    field public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCE = 22; // 0x16
-    field public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCES = 23; // 0x17
-    field public static final int MEDIA_CALL_SET_PLAYBACK_PARAMS = 24; // 0x18
-    field public static final int MEDIA_CALL_SET_PLAYBACK_SPEED = 25; // 0x19
-    field public static final int MEDIA_CALL_SET_PLAYER_VOLUME = 26; // 0x1a
-    field public static final int MEDIA_CALL_SET_SURFACE = 27; // 0x1b
-    field public static final int MEDIA_CALL_SET_SYNC_PARAMS = 28; // 0x1c
-    field public static final int MEDIA_CALL_SKIP_TO_NEXT = 29; // 0x1d
+    field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
+    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_RELEASE_DRM = 12; // 0xc
+    field public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13; // 0xd
+    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_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_PLAYBACK_SPEED = 25; // 0x19
+    field public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26; // 0x1a
+    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_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 = 5; // 0x5
+    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 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
@@ -24453,7 +24461,7 @@
 
   public static abstract class MediaPlayer2.MediaPlayer2EventCallback {
     ctor public MediaPlayer2.MediaPlayer2EventCallback();
-    method public void onCallComplete(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
+    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);
diff --git a/api/system-current.txt b/api/system-current.txt
index 43425cb..ab35312 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5261,12 +5261,13 @@
   }
 
   public class UiccSlotInfo implements android.os.Parcelable {
-    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
+    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int, boolean);
     method public int describeContents();
     method public java.lang.String getCardId();
     method public int getCardStateInfo();
     method public boolean getIsActive();
     method public boolean getIsEuicc();
+    method public boolean getIsExtendedApduSupported();
     method public int getLogicalSlotIdx();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
@@ -6127,19 +6128,24 @@
   }
 
   public final class ImsFeatureConfiguration implements android.os.Parcelable {
-    ctor public ImsFeatureConfiguration();
     method public int describeContents();
-    method public int[] getServiceFeatures();
+    method public java.util.Set<android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair> getServiceFeatures();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
   }
 
   public static class ImsFeatureConfiguration.Builder {
     ctor public ImsFeatureConfiguration.Builder();
-    method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int);
+    method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int, int);
     method public android.telephony.ims.stub.ImsFeatureConfiguration build();
   }
 
+  public static final class ImsFeatureConfiguration.FeatureSlotPair {
+    ctor public ImsFeatureConfiguration.FeatureSlotPair(int, int);
+    field public final int featureType;
+    field public final int slotId;
+  }
+
   public class ImsMultiEndpointImplBase {
     ctor public ImsMultiEndpointImplBase();
     method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9d67f4c..54ddd5d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,3 +1,12 @@
+package android {
+
+  public static final class Manifest.permission {
+    field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
+    field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+  }
+
+}
+
 package android.animation {
 
   public class ValueAnimator extends android.animation.Animator {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 9bfbd38..8ba35b7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -23,6 +23,7 @@
 
 import "frameworks/base/cmds/statsd/src/atom_field_options.proto";
 import "frameworks/base/core/proto/android/app/enums.proto";
+import "frameworks/base/core/proto/android/bluetooth/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
@@ -106,6 +107,9 @@
         KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
         AppDied app_died=65;
         ResourceConfigurationChanged resource_configuration_changed = 66;
+        BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
+        BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
+        BluetoothA2dpAudioStateChanged bluetooth_a2dp_audio_state_changed = 69;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -904,6 +908,63 @@
 }
 
 /**
+ * Logs when Bluetooth is enabled and disabled.
+ *
+ * Logged from:
+ *   services/core/java/com/android/server/BluetoothManagerService.java
+ */
+message BluetoothEnabledStateChanged {
+    repeated AttributionNode attribution_node = 1;
+    // Whether or not bluetooth is enabled on the device.
+    enum State {
+        UNKNOWN = 0;
+        ENABLED = 1;
+        DISABLED = 2;
+    }
+    optional State state = 2;
+    // The reason for being enabled/disabled.
+    // Eg. Airplane mode, crash, application request.
+    optional android.bluetooth.EnableDisableReasonEnum reason = 3;
+    // If the reason is an application request, this will be the package name.
+    optional string pkgName = 4;
+}
+
+/**
+ * Logs when a Bluetooth device connects and disconnects.
+ *
+ * Logged from:
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
+ */
+message BluetoothConnectionStateChanged {
+    // The state of the connection.
+    // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED.
+    optional android.bluetooth.ConnectionStateEnum state = 1;
+    // An identifier that can be used to match connect and disconnect events.
+    // Currently is last two bytes of a hash of a device level ID and
+    // the mac address of the bluetooth device that is connected.
+    optional int32 obfuscated_id = 2;
+    // The profile that is connected. Eg. GATT, A2DP, HEADSET.
+    // From android.bluetooth.BluetoothAdapter.java
+    optional int32 bt_profile = 3;
+}
+
+/**
+ * Logs when Bluetooth A2dp audio streaming state changes.
+ *
+ * Logged from:
+ *    TODO(b/73971848)
+ */
+message BluetoothA2dpAudioStateChanged {
+    // Whether or not audio is being played using Bluetooth A2dp.
+    enum State {
+        UNKNOWN = 0;
+        PLAY = 1;
+        STOP = 2;
+    }
+    optional State state = 1;
+}
+
+/**
  * Logs the duration of a davey (jank of >=700ms) when it occurs
  *
  * Logged from:
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index d0f55ab..f5310a4 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -68,12 +68,25 @@
     {
         lock_guard <mutex> lock(mMutex);
 
+        auto it = mConfigs.find(key);
+
+        const int numBytes = config.ByteSize();
+        vector<uint8_t> buffer(numBytes);
+        config.SerializeToArray(&buffer[0], numBytes);
+
+        const bool isDuplicate =
+            it != mConfigs.end() &&
+            StorageManager::hasIdenticalConfig(key, buffer);
+
+        // Update saved file on disk. We still update timestamp of file when
+        // there exists a duplicate configuration to avoid garbage collection.
+        update_saved_configs_locked(key, buffer, numBytes);
+
+        if (isDuplicate) return;
+
         // Add to set
         mConfigs.insert(key);
 
-        // Save to disk
-        update_saved_configs_locked(key, config);
-
         for (sp<ConfigListener> listener : mListeners) {
             broadcastList.push_back(listener);
         }
@@ -137,7 +150,6 @@
     {
         lock_guard <mutex> lock(mMutex);
 
-
         for (auto it = mConfigs.begin(); it != mConfigs.end();) {
             // Remove from map
             if (it->GetUid() == uid) {
@@ -230,16 +242,16 @@
     }
 }
 
-void ConfigManager::update_saved_configs_locked(const ConfigKey& key, const StatsdConfig& config) {
+void ConfigManager::update_saved_configs_locked(const ConfigKey& key,
+                                                const vector<uint8_t>& buffer,
+                                                const int numBytes) {
     // If there is a pre-existing config with same key we should first delete it.
     remove_saved_configs(key);
 
     // Then we save the latest config.
-    string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_SERVICE_DIR, time(nullptr),
-                                    key.GetUid(), (long long)key.GetId());
-    const int numBytes = config.ByteSize();
-    vector<uint8_t> buffer(numBytes);
-    config.SerializeToArray(&buffer[0], numBytes);
+    string file_name =
+        StringPrintf("%s/%ld_%d_%lld", STATS_SERVICE_DIR, time(nullptr),
+                     key.GetUid(), (long long)key.GetId());
     StorageManager::writeFile(file_name.c_str(), &buffer[0], numBytes);
 }
 
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index a0c1c1c..9a38188a 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -115,7 +115,9 @@
     /**
      * Save the configs to disk.
      */
-    void update_saved_configs_locked(const ConfigKey& key, const StatsdConfig& config);
+    void update_saved_configs_locked(const ConfigKey& key,
+                                     const std::vector<uint8_t>& buffer,
+                                     const int numBytes);
 
     /**
      * Remove saved configs from disk.
@@ -123,7 +125,7 @@
     void remove_saved_configs(const ConfigKey& key);
 
     /**
-     * The Configs that have been set. Each config should
+     * Config keys that have been set.
      */
     std::set<ConfigKey> mConfigs;
 
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 781eced..77387cb 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -245,6 +245,45 @@
     }
 }
 
+bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
+                                        const vector<uint8_t>& config) {
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
+                                             closedir);
+    if (dir == NULL) {
+        VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
+        return false;
+    }
+
+    const char* suffix =
+            StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str();
+
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* name = de->d_name;
+        if (name[0] == '.') {
+            continue;
+        }
+        size_t nameLen = strlen(name);
+        size_t suffixLen = strlen(suffix);
+        // There can be at most one file that matches this suffix (config key).
+        if (suffixLen <= nameLen &&
+                strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
+            int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
+                                  O_RDONLY | O_CLOEXEC);
+            if (fd != -1) {
+                string content;
+                if (android::base::ReadFdToString(fd, &content)) {
+                    vector<uint8_t> vec(content.begin(), content.end());
+                    if (vec == config) {
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+}
+
 void StorageManager::trimToFit(const char* path) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
     if (dir == NULL) {
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 6c8ed0a..13ce5c6 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -78,6 +78,12 @@
      * files, accumulation of outdated files.
      */
     static void trimToFit(const char* dir);
+
+    /**
+     * Returns true if there already exists identical configuration on device.
+     */
+    static bool hasIdenticalConfig(const ConfigKey& key,
+                                   const vector<uint8_t>& config);
 };
 
 }  // namespace statsd
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5687780..a1a10a5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -124,6 +124,7 @@
 import android.widget.Toolbar;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
@@ -7110,6 +7111,12 @@
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public final ActivityThread getActivityThread() {
+        return mMainThread;
+    }
+
     final void performCreate(Bundle icicle) {
         performCreate(icicle, null);
     }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 379944e..afcd515 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3722,16 +3722,27 @@
         //Slog.i(TAG, "Running services: " + mServices);
     }
 
-    ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide, String reason) {
+    ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
+            String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (localLOGV) Slog.v(TAG, "Performing resume of " + r
                 + " finished=" + r.activity.mFinished);
         if (r != null && !r.activity.mFinished) {
             if (r.getLifecycleState() == ON_RESUME) {
-                throw new IllegalStateException(
-                        "Trying to resume activity which is already resumed");
+                if (!finalStateRequest) {
+                    final RuntimeException e = new IllegalStateException(
+                            "Trying to resume activity which is already resumed");
+                    Slog.e(TAG, e.getMessage(), e);
+                    Slog.e(TAG, r.getStateString());
+                    // TODO(lifecycler): A double resume request is possible when an activity
+                    // receives two consequent transactions with relaunch requests and "resumed"
+                    // final state requests and the second relaunch is omitted. We still try to
+                    // handle two resume requests for the final state. For cases other than this
+                    // one, we don't expect it to happen.
+                }
+                return null;
             }
-            if (clearHide) {
+            if (finalStateRequest) {
                 r.hideForNow = false;
                 r.activity.mStartedActivity = false;
             }
@@ -3782,7 +3793,7 @@
     }
 
     @Override
-    public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
+    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
             String reason) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -3790,7 +3801,7 @@
         mSomeActivitiesChanged = true;
 
         // TODO Push resumeArgs into the activity for consideration
-        final ActivityClientRecord r = performResumeActivity(token, clearHide, reason);
+        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
 
         if (r != null) {
             final Activity a = r.activity;
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 6bc66ec..206495d 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -67,9 +67,16 @@
     public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
             int configChanges, PendingTransactionActions pendingActions, String reason);
 
-    /** Resume the activity. */
-    public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
-            String reason);
+    /**
+     * Resume the activity.
+     * @param token Target activity token.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param isForward Flag indicating if next transition is forward.
+     * @param reason Reason for performing this operation.
+     */
+    public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
+            boolean isForward, String reason);
 
     /** Stop the activity. */
     public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index af2fb71..d16bc97 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -48,7 +48,8 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
-        client.handleResumeActivity(token, true /* clearHide */, mIsForward, "RESUME_ACTIVITY");
+        client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
+                "RESUME_ACTIVITY");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 0e52b34..0d995e8 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -147,7 +147,10 @@
             pw.println("Executor:");
             dump(pw, prefix);
 
-            Slog.wtf(TAG, stringWriter.toString());
+            Slog.w(TAG, stringWriter.toString());
+
+            // Ignore requests for non-existent client records for now.
+            return;
         }
 
         // Cycle to the state right before the final requested state.
@@ -191,7 +194,7 @@
                     mTransactionHandler.handleStartActivity(r, mPendingActions);
                     break;
                 case ON_RESUME:
-                    mTransactionHandler.handleResumeActivity(r.token, false /* clearHide */,
+                    mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                             r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                     break;
                 case ON_PAUSE:
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index df32fb9..aa2cf46 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -16,7 +16,6 @@
 package android.app.slice;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -35,7 +34,6 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.StrictMode.ThreadPolicy;
@@ -46,7 +44,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
 
 /**
  * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
@@ -163,18 +160,10 @@
 
     private static final boolean DEBUG = false;
 
-    private String mBindingPkg;
-    private SliceManager mSliceManager;
+    private static final long SLICE_BIND_ANR = 2000;
 
-    /**
-     * Return the package name of the caller that initiated the binding request
-     * currently happening. The returned package will have been
-     * verified to belong to the calling UID. Returns {@code null} if not
-     * currently performing an {@link #onBindSlice(Uri, List)}.
-     */
-    public final @Nullable String getBindingPackage() {
-        return mBindingPkg;
-    }
+    private String mCallback;
+    private SliceManager mSliceManager;
 
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
@@ -183,12 +172,12 @@
     }
 
     /**
-     * Implemented to create a slice. Will be called on the main thread.
+     * Implemented to create a slice.
      * <p>
      * onBindSlice should return as quickly as possible so that the UI tied
      * to this slice can be responsive. No network or other IO will be allowed
      * during onBindSlice. Any loading that needs to be done should happen
-     * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
+     * in the background with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
      * when the app is ready to provide the complete data in onBindSlice.
      * <p>
      * The slice returned should have a spec that is compatible with one of
@@ -381,55 +370,32 @@
     }
 
     private Collection<Uri> handleGetDescendants(Uri uri) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
+        mCallback = "onGetSliceDescendants";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
             return onGetSliceDescendants(uri);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Collection<Uri>[] output = new Collection[1];
-            Handler.getMain().post(() -> {
-                output[0] = onGetSliceDescendants(uri);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-                return output[0];
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
     private void handlePinSlice(Uri sliceUri) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
+        mCallback = "onSlicePinned";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
             onSlicePinned(sliceUri);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Handler.getMain().post(() -> {
-                onSlicePinned(sliceUri);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
     private void handleUnpinSlice(Uri sliceUri) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
+        mCallback = "onSliceUnpinned";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
             onSliceUnpinned(sliceUri);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Handler.getMain().post(() -> {
-                onSliceUnpinned(sliceUri);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
@@ -447,21 +413,12 @@
                 return createPermissionSlice(getContext(), sliceUri, pkg);
             }
         }
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            return onBindSliceStrict(sliceUri, supportedSpecs, pkg);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Slice[] output = new Slice[1];
-            Handler.getMain().post(() -> {
-                output[0] = onBindSliceStrict(sliceUri, supportedSpecs, pkg);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-                return output[0];
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        mCallback = "onBindSlice";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            return onBindSliceStrict(sliceUri, supportedSpecs);
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
@@ -513,19 +470,21 @@
         }
     }
 
-    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs,
-            String callingPackage) {
+    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         try {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectAll()
                     .penaltyDeath()
                     .build());
-            mBindingPkg = callingPackage;
             return onBindSlice(sliceUri, supportedSpecs);
         } finally {
-            mBindingPkg = null;
             StrictMode.setThreadPolicy(oldPolicy);
         }
     }
+
+    private final Runnable mAnr = () -> {
+        Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
+        Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
+    };
 }
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index f47d464..72db33f 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -819,8 +819,7 @@
      * @param config A session configuration (see {@link SessionConfiguration}).
      *
      * @throws IllegalArgumentException In case the session configuration is invalid; or the output
-     *                                  configurations are empty; or the session configuration
-     *                                  executor is invalid.
+     *                                  configurations are empty.
      * @throws CameraAccessException In case the camera device is no longer connected or has
      *                               encountered a fatal error.
      * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
new file mode 100644
index 0000000..866f370
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * A dispatcher that replaces one argument with another; replaces any argument at an index
+ * with another argument.
+ *
+ * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
+ * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
+ * be something
+ * that's not an {@code int}.</p>
+ *
+ * @param <T>
+ *          source dispatch type, whose methods with {@link #dispatch} will be called
+ * @param <TArg>
+ *          argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
+ *          will be overriden to objects of this type
+ */
+public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
+
+    private final Dispatchable<T> mTarget;
+    private final int mArgumentIndex;
+    private final TArg mReplaceWith;
+
+    /**
+     * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
+     * after the argument is replaced.
+     *
+     * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
+     * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
+     * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
+     *
+     * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
+     * passed through with the arguments unchanged.</p>
+     *
+     * @param target destination dispatch type, methods will be redirected to this dispatcher
+     * @param argumentIndex the numeric index of the argument {@code >= 0}
+     * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
+     */
+    public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
+            TArg replaceWith) {
+        mTarget = checkNotNull(target, "target must not be null");
+        mArgumentIndex = checkArgumentNonnegative(argumentIndex,
+                "argumentIndex must not be negative");
+        mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
+    }
+
+    @Override
+    public Object dispatch(Method method, Object[] args) throws Throwable {
+
+        if (args.length > mArgumentIndex) {
+            args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
+            args[mArgumentIndex] = mReplaceWith;
+        }
+
+        return mTarget.dispatch(method, args);
+    }
+
+    private static Object[] arrayCopy(Object[] array) {
+        int length = array.length;
+        Object[] newArray = new Object[length];
+        for (int i = 0; i < length; ++i) {
+            newArray[i] = array[i];
+        }
+        return newArray;
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
new file mode 100644
index 0000000..fe575b2
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Broadcast a single dispatch into multiple other dispatchables.
+ *
+ * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
+ * see the same dispatch as well. The first target's return value is returned.</p>
+ *
+ * <p>This enables a single listener to be converted into a multi-listener.</p>
+ */
+public class BroadcastDispatcher<T> implements Dispatchable<T> {
+
+    private final List<Dispatchable<T>> mDispatchTargets;
+
+    /**
+     * Create a broadcast dispatcher from the supplied dispatch targets.
+     *
+     * @param dispatchTargets one or more targets to dispatch to
+     */
+    @SafeVarargs
+    public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
+        mDispatchTargets = Arrays.asList(
+                checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
+    }
+
+    @Override
+    public Object dispatch(Method method, Object[] args) throws Throwable {
+        Object result = null;
+        boolean gotResult = false;
+
+        for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
+            Object localResult = dispatchTarget.dispatch(method, args);
+
+            if (!gotResult) {
+                gotResult = true;
+                result = localResult;
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
new file mode 100644
index 0000000..753103f
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import java.lang.reflect.Method;
+
+/**
+ * Dynamically dispatch a method and its argument to some object.
+ *
+ * <p>This can be used to intercept method calls and do work around them, redirect work,
+ * or block calls entirely.</p>
+ */
+public interface Dispatchable<T> {
+    /**
+     * Dispatch the method and arguments to this object.
+     * @param method a method defined in class {@code T}
+     * @param args arguments corresponding to said {@code method}
+     * @return the object returned when invoking {@code method}
+     * @throws Throwable any exception that might have been raised while invoking the method
+     */
+    public Object dispatch(Method method, Object[] args) throws Throwable;
+}
diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
new file mode 100644
index 0000000..75f97e4
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Duck typing dispatcher; converts dispatch methods calls from one class to another by
+ * looking up equivalently methods at runtime by name.
+ *
+ * <p>For example, if two types have identical method names and arguments, but
+ * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
+ * made from one type to the other.</p>
+ *
+ * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
+ * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
+ */
+public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
+
+    private final MethodNameInvoker<T> mDuck;
+
+    /**
+     * Create a new duck typing dispatcher.
+     *
+     * @param target destination dispatch type, methods will be redirected to this dispatcher
+     * @param targetClass destination dispatch class, methods will be converted to this class's
+     */
+    public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
+        checkNotNull(targetClass, "targetClass must not be null");
+        checkNotNull(target, "target must not be null");
+
+        mDuck = new MethodNameInvoker<T>(target, targetClass);
+    }
+
+    @Override
+    public Object dispatch(Method method, Object[] args) {
+        return mDuck.invoke(method.getName(), args);
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
new file mode 100644
index 0000000..f8e9d49
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import android.hardware.camera2.utils.UncheckedThrow;
+import android.os.Handler;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Forward all interface calls into a handler by posting it as a {@code Runnable}.
+ *
+ * <p>All calls will return immediately; functions with return values will return a default
+ * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
+ *
+ * <p>Any exceptions thrown on the handler while trying to invoke a method
+ * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
+ * checked exceptions to be thrown will result in "undefined" behavior
+ * (although in practice it is usually thrown as normal).</p>
+ */
+public class HandlerDispatcher<T> implements Dispatchable<T> {
+
+    private static final String TAG = "HandlerDispatcher";
+
+    private final Dispatchable<T> mDispatchTarget;
+    private final Handler mHandler;
+
+    /**
+     * Create a dispatcher that forwards it's dispatch calls by posting
+     * them onto the {@code handler} as a {@code Runnable}.
+     *
+     * @param dispatchTarget the destination whose method calls will be redirected into the handler
+     * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
+     * @param <T> the type of the element you want to wrap.
+     * @return a dispatcher that will forward it's dispatch calls to a handler
+     */
+    public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
+        mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+        mHandler = checkNotNull(handler, "handler must not be null");
+    }
+
+    @Override
+    public Object dispatch(final Method method, final Object[] args) throws Throwable {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    mDispatchTarget.dispatch(method, args);
+                } catch (InvocationTargetException e) {
+                    Throwable t = e.getTargetException();
+                    // Potential UB. Hopefully 't' is a runtime exception.
+                    UncheckedThrow.throwAnyException(t);
+                } catch (IllegalAccessException e) {
+                    // Impossible
+                    Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
+                } catch (IllegalArgumentException e) {
+                    // Impossible
+                    Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
+                } catch (Throwable e) {
+                    UncheckedThrow.throwAnyException(e);
+                }
+            }
+        });
+
+        // TODO handle primitive return values that would avoid NPE if unboxed
+        return null;
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
new file mode 100644
index 0000000..ac5f526
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import android.hardware.camera2.utils.UncheckedThrow;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+
+public class InvokeDispatcher<T> implements Dispatchable<T> {
+
+    private static final String TAG = "InvocationSink";
+    private final T mTarget;
+
+    public InvokeDispatcher(T target) {
+        mTarget = checkNotNull(target, "target must not be null");
+    }
+
+    @Override
+    public Object dispatch(Method method, Object[] args) {
+        try {
+            return method.invoke(mTarget, args);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            // Potential UB. Hopefully 't' is a runtime exception.
+            UncheckedThrow.throwAnyException(t);
+        } catch (IllegalAccessException e) {
+            // Impossible
+            Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
+        } catch (IllegalArgumentException e) {
+            // Impossible
+            Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
+        }
+
+        // unreachable
+        return null;
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
new file mode 100644
index 0000000..8296b7a
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.hardware.camera2.utils.UncheckedThrow;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
+ *
+ * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
+ */
+public class MethodNameInvoker<T> {
+
+    private final Dispatchable<T> mTarget;
+    private final Class<T> mTargetClass;
+    private final Method[] mTargetClassMethods;
+    private final ConcurrentHashMap<String, Method> mMethods =
+            new ConcurrentHashMap<>();
+
+    /**
+     * Create a new method name invoker.
+     *
+     * @param target destination dispatch type, invokes will be redirected to this dispatcher
+     * @param targetClass destination dispatch class, the invoked methods will be from this class
+     */
+    public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
+        mTargetClass = targetClass;
+        mTargetClassMethods = targetClass.getMethods();
+        mTarget = target;
+    }
+
+    /**
+     * Invoke a method by its name.
+     *
+     * <p>If more than one method exists in {@code targetClass}, the first method with the right
+     * number of arguments will be used, and later calls will all use that method.</p>
+     *
+     * @param methodName
+     *          The name of the method, which will be matched 1:1 to the destination method
+     * @param params
+     *          Variadic parameter list.
+     * @return
+     *          The same kind of value that would normally be returned by calling {@code methodName}
+     *          statically.
+     *
+     * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
+     * @throws Throwable will rethrow anything that the target method would normally throw
+     */
+    @SuppressWarnings("unchecked")
+    public <K> K invoke(String methodName, Object... params) {
+        checkNotNull(methodName, "methodName must not be null");
+
+        Method targetMethod = mMethods.get(methodName);
+        if (targetMethod == null) {
+            for (Method method : mTargetClassMethods) {
+                // TODO future: match types of params if possible
+                if (method.getName().equals(methodName) &&
+                        (params.length == method.getParameterTypes().length) ) {
+                    targetMethod = method;
+                    mMethods.put(methodName, targetMethod);
+                    break;
+                }
+            }
+
+            if (targetMethod == null) {
+                throw new IllegalArgumentException(
+                        "Method " + methodName + " does not exist on class " + mTargetClass);
+            }
+        }
+
+        try {
+            return (K) mTarget.dispatch(targetMethod, params);
+        } catch (Throwable e) {
+            UncheckedThrow.throwAnyException(e);
+            // unreachable
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
new file mode 100644
index 0000000..fada075
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+
+import java.lang.reflect.Method;
+
+/**
+ * Do nothing when dispatching; follows the null object pattern.
+ */
+public class NullDispatcher<T> implements Dispatchable<T> {
+    /**
+     * Create a dispatcher that does nothing when dispatched to.
+     */
+    public NullDispatcher() {
+    }
+
+    /**
+     * Do nothing; all parameters are ignored.
+     */
+    @Override
+    public Object dispatch(Method method, Object[] args) {
+        return null;
+    }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index 9e4cb80..c9eecf1 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -15,17 +15,16 @@
  */
 package android.hardware.camera2.impl;
 
-import android.os.Binder;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.dispatch.Dispatchable;
+import android.hardware.camera2.dispatch.MethodNameInvoker;
 import android.view.Surface;
 
-import java.util.concurrent.Executor;
-
 import static com.android.internal.util.Preconditions.*;
 
 /**
@@ -35,86 +34,164 @@
  * to use our own proxy mechanism.</p>
  */
 public class CallbackProxies {
+
+    // TODO: replace with codegen
+
+    public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK {
+        private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy;
+
+        public DeviceStateCallbackProxy(
+                Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) {
+            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+            mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class);
+        }
+
+        @Override
+        public void onOpened(CameraDevice camera) {
+            mProxy.invoke("onOpened", camera);
+        }
+
+        @Override
+        public void onDisconnected(CameraDevice camera) {
+            mProxy.invoke("onDisconnected", camera);
+        }
+
+        @Override
+        public void onError(CameraDevice camera, int error) {
+            mProxy.invoke("onError", camera, error);
+        }
+
+        @Override
+        public void onUnconfigured(CameraDevice camera) {
+            mProxy.invoke("onUnconfigured", camera);
+        }
+
+        @Override
+        public void onActive(CameraDevice camera) {
+            mProxy.invoke("onActive", camera);
+        }
+
+        @Override
+        public void onBusy(CameraDevice camera) {
+            mProxy.invoke("onBusy", camera);
+        }
+
+        @Override
+        public void onClosed(CameraDevice camera) {
+            mProxy.invoke("onClosed", camera);
+        }
+
+        @Override
+        public void onIdle(CameraDevice camera) {
+            mProxy.invoke("onIdle", camera);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
+        private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
+
+        public DeviceCaptureCallbackProxy(
+                Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) {
+            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+            mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class);
+        }
+
+        @Override
+        public void onCaptureStarted(CameraDevice camera,
+                CaptureRequest request, long timestamp, long frameNumber) {
+            mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber);
+        }
+
+        @Override
+        public void onCapturePartial(CameraDevice camera,
+                CaptureRequest request, CaptureResult result) {
+            mProxy.invoke("onCapturePartial", camera, request, result);
+        }
+
+        @Override
+        public void onCaptureProgressed(CameraDevice camera,
+                CaptureRequest request, CaptureResult partialResult) {
+            mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
+        }
+
+        @Override
+        public void onCaptureCompleted(CameraDevice camera,
+                CaptureRequest request, TotalCaptureResult result) {
+            mProxy.invoke("onCaptureCompleted", camera, request, result);
+        }
+
+        @Override
+        public void onCaptureFailed(CameraDevice camera,
+                CaptureRequest request, CaptureFailure failure) {
+            mProxy.invoke("onCaptureFailed", camera, request, failure);
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(CameraDevice camera,
+                int sequenceId, long frameNumber) {
+            mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
+        }
+
+        @Override
+        public void onCaptureSequenceAborted(CameraDevice camera,
+                int sequenceId) {
+            mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
+        }
+
+        @Override
+        public void onCaptureBufferLost(CameraDevice camera,
+                CaptureRequest request, Surface target, long frameNumber) {
+            mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
+        }
+
+    }
+
     public static class SessionStateCallbackProxy
             extends CameraCaptureSession.StateCallback {
-        private final Executor mExecutor;
-        private final CameraCaptureSession.StateCallback mCallback;
+        private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy;
 
-        public SessionStateCallbackProxy(Executor executor,
-                CameraCaptureSession.StateCallback callback) {
-            mExecutor = checkNotNull(executor, "executor must not be null");
-            mCallback = checkNotNull(callback, "callback must not be null");
+        public SessionStateCallbackProxy(
+                Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) {
+            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+            mProxy = new MethodNameInvoker<>(dispatchTarget,
+                    CameraCaptureSession.StateCallback.class);
         }
 
         @Override
         public void onConfigured(CameraCaptureSession session) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onConfigured(session));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onConfigured", session);
         }
 
 
         @Override
         public void onConfigureFailed(CameraCaptureSession session) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onConfigureFailed(session));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onConfigureFailed", session);
         }
 
         @Override
         public void onReady(CameraCaptureSession session) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onReady(session));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onReady", session);
         }
 
         @Override
         public void onActive(CameraCaptureSession session) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onActive(session));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onActive", session);
         }
 
         @Override
         public void onCaptureQueueEmpty(CameraCaptureSession session) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onCaptureQueueEmpty", session);
         }
 
         @Override
         public void onClosed(CameraCaptureSession session) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onClosed(session));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onClosed", session);
         }
 
         @Override
         public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            mProxy.invoke("onSurfacePrepared", session, surface);
         }
 
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index f3f6c84..8b8bbc3 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -20,18 +20,20 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
+import android.hardware.camera2.dispatch.BroadcastDispatcher;
+import android.hardware.camera2.dispatch.DuckTypingDispatcher;
+import android.hardware.camera2.dispatch.HandlerDispatcher;
+import android.hardware.camera2.dispatch.InvokeDispatcher;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.TaskDrainer;
 import android.hardware.camera2.utils.TaskSingleDrainer;
-import android.os.Binder;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.util.Log;
 import android.view.Surface;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
 import static com.android.internal.util.Preconditions.*;
@@ -49,11 +51,11 @@
     private final Surface mInput;
     /**
      * User-specified state callback, used for outgoing events; calls to this object will be
-     * automatically invoked via {@code mStateExecutor}.
+     * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
      */
     private final CameraCaptureSession.StateCallback mStateCallback;
-    /** User-specified state executor used for outgoing state callback events */
-    private final Executor mStateExecutor;
+    /** User-specified state handler used for outgoing state callback events */
+    private final Handler mStateHandler;
 
     /** Internal camera device; used to translate calls into existing deprecated API */
     private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
@@ -85,7 +87,7 @@
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
     CameraCaptureSessionImpl(int id, Surface input,
-            CameraCaptureSession.StateCallback callback, Executor stateExecutor,
+            CameraCaptureSession.StateCallback callback, Handler stateHandler,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
             Handler deviceStateHandler, boolean configureSuccess) {
         if (callback == null) {
@@ -96,8 +98,8 @@
         mIdString = String.format("Session %d: ", mId);
 
         mInput = input;
-        mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
-        mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
+        mStateHandler = checkHandler(stateHandler);
+        mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
 
         mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
         mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
@@ -108,12 +110,12 @@
          * This ensures total ordering between CameraDevice.StateCallback and
          * CameraDeviceImpl.CaptureCallback events.
          */
-        mSequenceDrainer = new TaskDrainer<>(new HandlerExecutor(mDeviceHandler),
-                new SequenceDrainListener(), /*name*/"seq");
-        mIdleDrainer = new TaskSingleDrainer(new HandlerExecutor(mDeviceHandler),
-                new IdleDrainListener(), /*name*/"idle");
-        mAbortDrainer = new TaskSingleDrainer(new HandlerExecutor(mDeviceHandler),
-                new AbortDrainListener(), /*name*/"abort");
+        mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+                /*name*/"seq");
+        mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+                /*name*/"idle");
+        mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+                /*name*/"abort");
 
         // CameraDevice should call configureOutputs and have it finish before constructing us
 
@@ -444,140 +446,114 @@
     }
 
     /**
-     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
+     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
      */
-    private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
-        return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
+    private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
+        InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
+        HandlerDispatcher<StateCallback> handlerPassthrough =
+                new HandlerDispatcher<>(userCallbackSink, handler);
+
+        return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
     }
 
     /**
      * Forward callbacks from
      * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
      *
+     * <p>In particular, all calls are automatically split to go both to our own
+     * internal callback, and to the user-specified callback (by transparently posting
+     * to the user-specified handler).</p>
+     *
      * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
      */
     @SuppressWarnings("deprecation")
     private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
             Handler handler, CaptureCallback callback) {
-        final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
-                handler) : null;
+        CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
 
-        return new CameraDeviceImpl.CaptureCallback() {
             @Override
             public void onCaptureStarted(CameraDevice camera,
                     CaptureRequest request, long timestamp, long frameNumber) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureStarted(
-                                    CameraCaptureSessionImpl.this, request, timestamp,
-                                    frameNumber));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
+                // Do nothing
             }
 
             @Override
             public void onCapturePartial(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureResult result) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCapturePartial(
-                                    CameraCaptureSessionImpl.this, request, result));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
+                // Do nothing
             }
 
             @Override
             public void onCaptureProgressed(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureProgressed(
-                                    CameraCaptureSessionImpl.this, request, partialResult));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
+                // Do nothing
             }
 
             @Override
             public void onCaptureCompleted(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureCompleted(
-                                    CameraCaptureSessionImpl.this, request, result));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
+                // Do nothing
             }
 
             @Override
             public void onCaptureFailed(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureFailed(
-                                    CameraCaptureSessionImpl.this, request, failure));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
+                // Do nothing
             }
 
             @Override
             public void onCaptureSequenceCompleted(CameraDevice camera,
                     int sequenceId, long frameNumber) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureSequenceCompleted(
-                                    CameraCaptureSessionImpl.this, sequenceId, frameNumber));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
                 finishPendingSequence(sequenceId);
             }
 
             @Override
             public void onCaptureSequenceAborted(CameraDevice camera,
                     int sequenceId) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureSequenceAborted(
-                                    CameraCaptureSessionImpl.this, sequenceId));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
                 finishPendingSequence(sequenceId);
             }
 
             @Override
             public void onCaptureBufferLost(CameraDevice camera,
                     CaptureRequest request, Surface target, long frameNumber) {
-                if ((callback != null) && (executor != null)) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> callback.onCaptureBufferLost(
-                                    CameraCaptureSessionImpl.this, request, target, frameNumber));
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-                }
+                // Do nothing
             }
+
         };
+
+        /*
+         * Split the calls from the device callback into local callback and the following chain:
+         * - replace the first CameraDevice arg with a CameraCaptureSession
+         * - duck type from device callback to session callback
+         * - then forward the call to a handler
+         * - then finally invoke the destination method on the session callback object
+         */
+        if (callback == null) {
+            // OK: API allows the user to not specify a callback, and the handler may
+            // also be null in that case. Collapse whole dispatch chain to only call the local
+            // callback
+            return localCallback;
+        }
+
+        InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
+                new InvokeDispatcher<>(localCallback);
+
+        InvokeDispatcher<CaptureCallback> userCallbackSink =
+                new InvokeDispatcher<>(callback);
+        HandlerDispatcher<CaptureCallback> handlerPassthrough =
+                new HandlerDispatcher<>(userCallbackSink, handler);
+        DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
+                = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
+        ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
+                replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
+                        /*argumentIndex*/0, this);
+
+        BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
+                new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
+                    replaceDeviceWithSession,
+                    localSink);
+
+        return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 4ee08ba..06c2c25 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -33,7 +33,6 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -60,14 +59,14 @@
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
     CameraConstrainedHighSpeedCaptureSessionImpl(int id,
-            CameraCaptureSession.StateCallback callback, Executor stateExecutor,
+            CameraCaptureSession.StateCallback callback, Handler stateHandler,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
             Handler deviceStateHandler, boolean configureSuccess,
             CameraCharacteristics characteristics) {
         mCharacteristics = characteristics;
         CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
         mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
-                stateExecutor, deviceImpl, deviceStateHandler, configureSuccess);
+                stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
     }
 
     @Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index b328bb1..511fa43 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -35,10 +35,8 @@
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.hardware.camera2.utils.SurfaceUtils;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -60,7 +58,6 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.Executor;
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -504,9 +501,8 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(null, outConfigurations, callback,
-                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
-                /*sessionParams*/ null);
+        createCaptureSessionInternal(null, outConfigurations, callback, handler,
+                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
     }
 
     @Override
@@ -521,7 +517,7 @@
         // OutputConfiguration objects are immutable, but need to have our own array
         List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
 
-        createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
+        createCaptureSessionInternal(null, currentOutputs, callback, handler,
                 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
     }
 
@@ -541,9 +537,8 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(inputConfig, outConfigurations, callback,
-                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
-                /*sessionParams*/ null);
+        createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
+                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
     }
 
     @Override
@@ -571,8 +566,8 @@
             currentOutputs.add(new OutputConfiguration(output));
         }
         createCaptureSessionInternal(inputConfig, currentOutputs,
-                callback, checkAndWrapHandler(handler),
-                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+                callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+                /*sessionParams*/ null);
     }
 
     @Override
@@ -587,8 +582,7 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(null, outConfigurations, callback,
-                checkAndWrapHandler(handler),
+        createCaptureSessionInternal(null, outConfigurations, callback, handler,
                 /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
                 /*sessionParams*/ null);
     }
@@ -603,8 +597,8 @@
         for (OutputConfiguration output : outputs) {
             currentOutputs.add(new OutputConfiguration(output));
         }
-        createCaptureSessionInternal(inputConfig, currentOutputs, callback,
-                checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
+        createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
+                /*sessionParams*/ null);
     }
 
     @Override
@@ -618,17 +612,14 @@
         if (outputConfigs == null) {
             throw new IllegalArgumentException("Invalid output configurations");
         }
-        if (config.getExecutor() == null) {
-            throw new IllegalArgumentException("Invalid executor");
-        }
         createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
-                config.getStateCallback(), config.getExecutor(), config.getSessionType(),
+                config.getStateCallback(), config.getHandler(), config.getSessionType(),
                 config.getSessionParameters());
     }
 
     private void createCaptureSessionInternal(InputConfiguration inputConfig,
             List<OutputConfiguration> outputConfigurations,
-            CameraCaptureSession.StateCallback callback, Executor executor,
+            CameraCaptureSession.StateCallback callback, Handler handler,
             int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
         synchronized(mInterfaceLock) {
             if (DEBUG) {
@@ -682,11 +673,12 @@
                 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
 
                 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
-                        callback, executor, this, mDeviceHandler, configureSuccess,
+                        callback, handler, this, mDeviceHandler, configureSuccess,
                         mCharacteristics);
             } else {
                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
-                        callback, executor, this, mDeviceHandler, configureSuccess);
+                        callback, handler, this, mDeviceHandler,
+                        configureSuccess);
             }
 
             // TODO: wait until current session closes, then create the new session
@@ -971,12 +963,7 @@
                         }
                     }
                 };
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    holder.getExecutor().execute(resultDispatch);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                holder.getHandler().post(resultDispatch);
             } else {
                 Log.w(TAG, String.format(
                         "did not register callback to request %d",
@@ -997,9 +984,9 @@
     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
             Handler handler, boolean repeating) throws CameraAccessException {
 
-        // Need a valid executor, or current thread needs to have a looper, if
+        // Need a valid handler, or current thread needs to have a looper, if
         // callback is valid
-        Executor executor = getExecutor(handler, callback);
+        handler = checkHandler(handler, callback);
 
         // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
         // the surface isn't a physical stream surface for reprocessing request
@@ -1053,7 +1040,7 @@
             if (callback != null) {
                 mCaptureCallbackMap.put(requestInfo.getRequestId(),
                         new CaptureCallbackHolder(
-                            callback, requestList, executor, repeating, mNextSessionId - 1));
+                            callback, requestList, handler, repeating, mNextSessionId - 1));
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
@@ -1367,7 +1354,7 @@
         private final boolean mRepeating;
         private final CaptureCallback mCallback;
         private final List<CaptureRequest> mRequestList;
-        private final Executor mExecutor;
+        private final Handler mHandler;
         private final int mSessionId;
         /**
          * <p>Determine if the callback holder is for a constrained high speed request list that
@@ -1379,13 +1366,13 @@
         private final boolean mHasBatchedOutputs;
 
         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
-                Executor executor, boolean repeating, int sessionId) {
-            if (callback == null || executor == null) {
+                Handler handler, boolean repeating, int sessionId) {
+            if (callback == null || handler == null) {
                 throw new UnsupportedOperationException(
                     "Must have a valid handler and a valid callback");
             }
             mRepeating = repeating;
-            mExecutor = executor;
+            mHandler = handler;
             mRequestList = new ArrayList<CaptureRequest>(requestList);
             mCallback = callback;
             mSessionId = sessionId;
@@ -1438,8 +1425,8 @@
             return getRequest(0);
         }
 
-        public Executor getExecutor() {
-            return mExecutor;
+        public Handler getHandler() {
+            return mHandler;
         }
 
         public int getSessionId() {
@@ -1823,12 +1810,7 @@
                         }
                     }
                 };
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    holder.getExecutor().execute(resultDispatch);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                holder.getHandler().post(resultDispatch);
             }
         }
     }
@@ -1879,7 +1861,7 @@
         private void scheduleNotifyError(int code) {
             mInError = true;
             CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
-                        CameraDeviceCallbacks::notifyError, this, code));
+                    CameraDeviceCallbacks::notifyError, this, code));
         }
 
         private void notifyError(int code) {
@@ -1947,41 +1929,36 @@
                 if (isClosed()) return;
 
                 // Dispatch capture start notice
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    holder.getExecutor().execute(
-                        new Runnable() {
-                            @Override
-                            public void run() {
-                                if (!CameraDeviceImpl.this.isClosed()) {
-                                    final int subsequenceId = resultExtras.getSubsequenceId();
-                                    final CaptureRequest request = holder.getRequest(subsequenceId);
+                holder.getHandler().post(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()) {
+                                final int subsequenceId = resultExtras.getSubsequenceId();
+                                final CaptureRequest request = holder.getRequest(subsequenceId);
 
-                                    if (holder.hasBatchedOutputs()) {
-                                        // Send derived onCaptureStarted for requests within the
-                                        // batch
-                                        final Range<Integer> fpsRange =
-                                            request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
-                                        for (int i = 0; i < holder.getRequestCount(); i++) {
-                                            holder.getCallback().onCaptureStarted(
-                                                CameraDeviceImpl.this,
-                                                holder.getRequest(i),
-                                                timestamp - (subsequenceId - i) *
-                                                NANO_PER_SECOND/fpsRange.getUpper(),
-                                                frameNumber - (subsequenceId - i));
-                                        }
-                                    } else {
+                                if (holder.hasBatchedOutputs()) {
+                                    // Send derived onCaptureStarted for requests within the batch
+                                    final Range<Integer> fpsRange =
+                                        request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+                                    for (int i = 0; i < holder.getRequestCount(); i++) {
                                         holder.getCallback().onCaptureStarted(
                                             CameraDeviceImpl.this,
-                                            holder.getRequest(resultExtras.getSubsequenceId()),
-                                            timestamp, frameNumber);
+                                            holder.getRequest(i),
+                                            timestamp - (subsequenceId - i) *
+                                            NANO_PER_SECOND/fpsRange.getUpper(),
+                                            frameNumber - (subsequenceId - i));
                                     }
+                                } else {
+                                    holder.getCallback().onCaptureStarted(
+                                        CameraDeviceImpl.this,
+                                        holder.getRequest(resultExtras.getSubsequenceId()),
+                                        timestamp, frameNumber);
                                 }
                             }
-                        });
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                        }
+                    });
+
             }
         }
 
@@ -2134,12 +2111,7 @@
                     finalResult = resultAsCapture;
                 }
 
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    holder.getExecutor().execute(resultDispatch);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                holder.getHandler().post(resultDispatch);
 
                 // Collect the partials for a total result; or mark the frame as totally completed
                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
@@ -2235,12 +2207,7 @@
                         }
                     };
                     // Dispatch the failure callback
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
-                        holder.getExecutor().execute(failureDispatch);
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
+                    holder.getHandler().post(failureDispatch);
                 }
             } else {
                 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
@@ -2280,12 +2247,7 @@
                 checkAndFireSequenceComplete();
 
                 // Dispatch the failure callback
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    holder.getExecutor().execute(failureDispatch);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                holder.getHandler().post(failureDispatch);
             }
 
         }
@@ -2293,37 +2255,6 @@
     } // public class CameraDeviceCallbacks
 
     /**
-     * Instantiate a new Executor.
-     *
-     * <p>If the callback isn't null, check the handler and instantiate a new executor,
-     * otherwise instantiate a new executor in case handler is valid.</p>
-     */
-    static <T> Executor getExecutor(Handler handler, T callback) {
-        if (callback != null) {
-            return checkAndWrapHandler(handler);
-        }
-
-        if (handler != null) {
-            return new HandlerExecutor(handler);
-        }
-
-        return null;
-    }
-
-    /**
-     * Wrap Handler in Executor.
-     *
-     * <p>
-     * If handler is null, get the current thread's
-     * Looper to create a Executor with. If no looper exists, throw
-     * {@code IllegalArgumentException}.
-     * </p>
-     */
-    static Executor checkAndWrapHandler(Handler handler) {
-        return new HandlerExecutor(checkHandler(handler));
-    }
-
-    /**
      * Default handler management.
      *
      * <p>
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 7bdb4a2..a79a6c1 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,10 @@
 
 package android.hardware.camera2.params;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.IntDef;
+import android.os.Handler;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -31,7 +31,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.ArrayList;
-import java.util.concurrent.Executor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -79,7 +78,7 @@
     private List<OutputConfiguration> mOutputConfigurations;
     private CameraCaptureSession.StateCallback mStateCallback;
     private int mSessionType;
-    private Executor mExecutor = null;
+    private Handler mHandler = null;
     private InputConfiguration mInputConfig = null;
     private CaptureRequest mSessionParameters = null;
 
@@ -88,9 +87,10 @@
      *
      * @param sessionType The session type.
      * @param outputs A list of output configurations for the capture session.
-     * @param executor The executor which should be used to invoke the callback. In general it is
-     *                 recommended that camera operations are not done on the main (UI) thread.
      * @param cb A state callback interface implementation.
+     * @param handler The handler on which the callback will be invoked. If it is
+     *                set to null, the callback will be invoked on the current thread's
+     *                {@link android.os.Looper looper}.
      *
      * @see #SESSION_REGULAR
      * @see #SESSION_HIGH_SPEED
@@ -101,12 +101,11 @@
      */
     public SessionConfiguration(@SessionMode int sessionType,
             @NonNull List<OutputConfiguration> outputs,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull CameraCaptureSession.StateCallback cb) {
+            @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) {
         mSessionType = sessionType;
         mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
         mStateCallback = cb;
-        mExecutor = executor;
+        mHandler = handler;
     }
 
     /**
@@ -137,12 +136,14 @@
     }
 
     /**
-     * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
+     * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
      *
-     * @return The Executor on which the callback will be invoked.
+     * @return The handler on which the callback will be invoked. If it is
+     *         set to null, the callback will be invoked on the current thread's
+     *         {@link android.os.Looper looper}.
      */
-    public Executor getExecutor() {
-        return mExecutor;
+    public Handler getHandler() {
+        return mHandler;
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java
index e71f26a..ed30ff3 100644
--- a/core/java/android/hardware/camera2/utils/TaskDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java
@@ -15,11 +15,11 @@
  */
 package android.hardware.camera2.utils;
 
+import android.os.Handler;
 import android.util.Log;
 
 import java.util.HashSet;
 import java.util.Set;
-import java.util.concurrent.Executor;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -55,7 +55,7 @@
     private static final String TAG = "TaskDrainer";
     private final boolean DEBUG = false;
 
-    private final Executor mExecutor;
+    private final Handler mHandler;
     private final DrainListener mListener;
     private final String mName;
 
@@ -73,27 +73,28 @@
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code executor}.
+     * via the {@code handler}.
      *
-     * @param executor a non-{@code null} executor to use for listener execution
+     * @param handler a non-{@code null} handler to use to post runnables to
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      */
-    public TaskDrainer(Executor executor, DrainListener listener) {
-        mExecutor = checkNotNull(executor, "executor must not be null");
+    public TaskDrainer(Handler handler, DrainListener listener) {
+        mHandler = checkNotNull(handler, "handler must not be null");
         mListener = checkNotNull(listener, "listener must not be null");
         mName = null;
     }
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code executor}.
+     * via the {@code handler}.
      *
-     * @param executor a non-{@code null} executor to use for listener execution
+     * @param handler a non-{@code null} handler to use to post runnables to
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      * @param name an optional name used for debug logging
      */
-    public TaskDrainer(Executor executor, DrainListener listener, String name) {
-        mExecutor = checkNotNull(executor, "executor must not be null");
+    public TaskDrainer(Handler handler, DrainListener listener, String name) {
+        // XX: Probably don't need a handler at all here
+        mHandler = checkNotNull(handler, "handler must not be null");
         mListener = checkNotNull(listener, "listener must not be null");
         mName = name;
     }
@@ -199,12 +200,15 @@
     }
 
     private void postDrained() {
-        mExecutor.execute(() -> {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
                 if (DEBUG) {
                     Log.v(TAG + "[" + mName + "]", "onDrained");
                 }
 
                 mListener.onDrained();
+            }
         });
     }
 }
diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
index 9615450..f6272c9 100644
--- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
+++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -16,8 +16,7 @@
 package android.hardware.camera2.utils;
 
 import android.hardware.camera2.utils.TaskDrainer.DrainListener;
-
-import java.util.concurrent.Executor;
+import android.os.Handler;
 
 /**
  * Keep track of a single concurrent task starting and finishing;
@@ -39,25 +38,25 @@
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code executor}.
+     * via the {@code handler}.
      *
-     * @param executor a non-{@code null} executor to use for listener execution
+     * @param handler a non-{@code null} handler to use to post runnables to
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      */
-    public TaskSingleDrainer(Executor executor, DrainListener listener) {
-        mTaskDrainer = new TaskDrainer<>(executor, listener);
+    public TaskSingleDrainer(Handler handler, DrainListener listener) {
+        mTaskDrainer = new TaskDrainer<>(handler, listener);
     }
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code executor}.
+     * via the {@code handler}.
      *
-     * @param executor a non-{@code null} executor to use for listener execution
+     * @param handler a non-{@code null} handler to use to post runnables to
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      * @param name an optional name used for debug logging
      */
-    public TaskSingleDrainer(Executor executor, DrainListener listener, String name) {
-        mTaskDrainer = new TaskDrainer<>(executor, listener, name);
+    public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
+        mTaskDrainer = new TaskDrainer<>(handler, listener, name);
     }
 
     /**
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 1fd9423..1e75bf4 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -20,8 +20,9 @@
 oneway interface INetdEventCallback {
 
     // Possible addNetdEventCallback callers.
-    const int CALLBACK_CALLER_DEVICE_POLICY = 0;
-    const int CALLBACK_CALLER_NETWORK_WATCHLIST = 1;
+    const int CALLBACK_CALLER_CONNECTIVITY_SERVICE = 0;
+    const int CALLBACK_CALLER_DEVICE_POLICY = 1;
+    const int CALLBACK_CALLER_NETWORK_WATCHLIST = 2;
 
     /**
      * Reports a single DNS lookup function call.
@@ -39,6 +40,18 @@
             int uid);
 
     /**
+     * Represents a private DNS validation success or failure.
+     * This method must not block or perform long-running operations.
+     *
+     * @param netId the ID of the network the validation was performed on.
+     * @param ipAddress the IP address for which validation was performed.
+     * @param hostname the hostname for which validation was performed.
+     * @param validated whether or not validation was successful.
+     */
+    void onPrivateDnsValidationEvent(int netId, String ipAddress, String hostname,
+            boolean validated);
+
+    /**
      * Reports a single connect library call.
      * This method must not block or perform long-running operations.
      *
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index c568b6f..140336e 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -49,7 +49,8 @@
  * </ul>
  *
  * <P> The minimum permission needed to access this content provider is
- * {@link android.Manifest.permission#ADD_VOICEMAIL}
+ * {@link android.Manifest.permission#ADD_VOICEMAIL} or carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
  *
  * <P>Voicemails are inserted by what is called as a "voicemail source"
  * application, which is responsible for syncing voicemail data between a remote
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index 19a018e..3d8b74b 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -301,12 +301,6 @@
      *         provide a new logical channel.
      */
     public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
-
-        if ((mReader.getName().startsWith("SIM")) && (aid == null)) {
-            Log.e(TAG, "NULL AID not supported on " + mReader.getName());
-            return null;
-        }
-
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 75cdd49..5b2cc81 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -16,13 +16,26 @@
 
 package android.view;
 
+import static android.app.RemoteAnimationTargetProto.CLIP_RECT;
+import static android.app.RemoteAnimationTargetProto.CONTENT_INSETS;
+import static android.app.RemoteAnimationTargetProto.IS_TRANSLUCENT;
+import static android.app.RemoteAnimationTargetProto.LEASH;
+import static android.app.RemoteAnimationTargetProto.MODE;
+import static android.app.RemoteAnimationTargetProto.POSITION;
+import static android.app.RemoteAnimationTargetProto.PREFIX_ORDER_INDEX;
+import static android.app.RemoteAnimationTargetProto.SOURCE_CONTAINER_BOUNDS;
+import static android.app.RemoteAnimationTargetProto.TASK_ID;
+import static android.app.RemoteAnimationTargetProto.WINDOW_CONFIGURATION;
+
 import android.annotation.IntDef;
 import android.app.WindowConfiguration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -164,6 +177,35 @@
         dest.writeBoolean(isNotInRecents);
     }
 
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("mode="); pw.print(mode);
+        pw.print(" taskId="); pw.print(taskId);
+        pw.print(" isTranslucent="); pw.print(isTranslucent);
+        pw.print(" clipRect="); clipRect.printShortString(pw);
+        pw.print(" contentInsets="); contentInsets.printShortString(pw);
+        pw.print(" prefixOrderIndex="); pw.print(prefixOrderIndex);
+        pw.print(" position="); position.printShortString(pw);
+        pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw);
+        pw.println();
+        pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
+        pw.print(prefix); pw.print("leash="); pw.println(leash);
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(TASK_ID, taskId);
+        proto.write(MODE, mode);
+        leash.writeToProto(proto, LEASH);
+        proto.write(IS_TRANSLUCENT, isTranslucent);
+        clipRect.writeToProto(proto, CLIP_RECT);
+        contentInsets.writeToProto(proto, CONTENT_INSETS);
+        proto.write(PREFIX_ORDER_INDEX, prefixOrderIndex);
+        position.writeToProto(proto, POSITION);
+        sourceContainerBounds.writeToProto(proto, SOURCE_CONTAINER_BOUNDS);
+        windowConfiguration.writeToProto(proto, WINDOW_CONFIGURATION);
+        proto.end(token);
+    }
+
     public static final Creator<RemoteAnimationTarget> CREATOR
             = new Creator<RemoteAnimationTarget>() {
         public RemoteAnimationTarget createFromParcel(Parcel in) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bf0e2eb..b624870 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6546,7 +6546,7 @@
             } finally {
                 // Set it to already called so it's not called twice when called by
                 // performClickInternal()
-                mPrivateFlags |= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
+                mPrivateFlags &= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
             }
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6fe64a0..f77a6b7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -291,6 +291,7 @@
  * @attr ref android.R.styleable#TextView_drawableTintMode
  * @attr ref android.R.styleable#TextView_lineSpacingExtra
  * @attr ref android.R.styleable#TextView_lineSpacingMultiplier
+ * @attr ref android.R.styleable#TextView_justificationMode
  * @attr ref android.R.styleable#TextView_marqueeRepeatLimit
  * @attr ref android.R.styleable#TextView_inputType
  * @attr ref android.R.styleable#TextView_imeOptions
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 9d4b5aa..c71e505 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Process;
 import android.os.storage.StorageManager;
@@ -276,9 +277,12 @@
         readPermissions(Environment.buildPath(
                 Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
 
-        // Allow Vendor to customize system configs around libs, features, permissions and apps
-        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
-                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
+        // Vendors are only allowed to customze libs, features and privapp permissions
+        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
+            // For backward compatibility
+            vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
+        }
         readPermissions(Environment.buildPath(
                 Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
         readPermissions(Environment.buildPath(
@@ -659,6 +663,8 @@
                     }
                     XmlUtils.skipCurrentTag(parser);
                 } else {
+                    Slog.w(TAG, "Tag " + name + " is unknown or not allowed in "
+                            + permFile.getParent());
                     XmlUtils.skipCurrentTag(parser);
                     continue;
                 }
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index 3d3a3d0..fdba2f3 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -32,6 +32,12 @@
     }
 
     @Override
+    public void onPrivateDnsValidationEvent(int netId, String ipAddress,
+            String hostname, boolean validated) {
+        // default no-op
+    }
+
+    @Override
     public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
         // default no-op
     }
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5498a93..ce4e384 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -921,6 +921,28 @@
 
     SkBitmap skbitmap;
     bitmap->getSkBitmap(&skbitmap);
+    if (skbitmap.colorType() == kRGBA_F16_SkColorType) {
+        // Convert to P3 before encoding. This matches SkAndroidCodec::computeOutputColorSpace
+        // for wide gamuts.
+        auto cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                        SkColorSpace::kDCIP3_D65_Gamut);
+        auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType)
+                                   .makeColorSpace(std::move(cs));
+        SkBitmap p3;
+        if (!p3.tryAllocPixels(info)) {
+            return JNI_FALSE;
+        }
+        auto xform = SkColorSpaceXform::New(skbitmap.colorSpace(), info.colorSpace());
+        if (!xform) {
+            return JNI_FALSE;
+        }
+        if (!xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, p3.getPixels(),
+                          SkColorSpaceXform::kRGBA_F16_ColorFormat, skbitmap.getPixels(),
+                          info.width() * info.height(), kUnpremul_SkAlphaType)) {
+            return JNI_FALSE;
+        }
+        skbitmap = p3;
+    }
     return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE;
 }
 
diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto
new file mode 100644
index 0000000..c4ffe8c
--- /dev/null
+++ b/core/proto/android/server/animationadapter.proto
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/graphics/point.proto";
+import "frameworks/base/core/proto/android/view/remote_animation_target.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+package com.android.server.wm.proto;
+option java_multiple_files = true;
+
+message AnimationAdapterProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional LocalAnimationAdapterProto local = 1;
+  optional RemoteAnimationAdapterWrapperProto remote = 2;
+}
+
+/* represents RemoteAnimationAdapterWrapper */
+message RemoteAnimationAdapterWrapperProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional .android.view.RemoteAnimationTargetProto target = 1;
+}
+
+/* represents LocalAnimationAdapter */
+message LocalAnimationAdapterProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional AnimationSpecProto animation_spec = 1;
+}
+
+message AnimationSpecProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional WindowAnimationSpecProto window = 1;
+  optional MoveAnimationSpecProto move = 2;
+  optional AlphaAnimationSpecProto alpha = 3;
+}
+
+/* represents WindowAnimationSpec */
+message WindowAnimationSpecProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional string animation = 1;
+}
+
+/* represents MoveAnimationSpec*/
+message MoveAnimationSpecProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional .android.graphics.PointProto from = 1;
+  optional .android.graphics.PointProto to = 2;
+  optional int64 duration = 3;
+}
+
+/* represents AlphaAnimationSpec */
+message AlphaAnimationSpecProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional float from = 1;
+  optional float to = 2;
+  optional int64 duration = 3;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/surfaceanimator.proto b/core/proto/android/server/surfaceanimator.proto
index 7f7839e..dcc2b34 100644
--- a/core/proto/android/server/surfaceanimator.proto
+++ b/core/proto/android/server/surfaceanimator.proto
@@ -16,6 +16,7 @@
 
 syntax = "proto2";
 
+import "frameworks/base/core/proto/android/server/animationadapter.proto";
 import "frameworks/base/core/proto/android/view/surfacecontrol.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
@@ -28,7 +29,8 @@
 message SurfaceAnimatorProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional string animation_adapter = 1;
+  reserved 1; // Was string animation_adapter = 1
   optional .android.view.SurfaceControlProto leash = 2;
   optional bool animation_start_delayed = 3;
+  optional AnimationAdapterProto animation_adapter = 4;
 }
\ No newline at end of file
diff --git a/core/proto/android/view/remote_animation_target.proto b/core/proto/android/view/remote_animation_target.proto
new file mode 100644
index 0000000..d5da0a9
--- /dev/null
+++ b/core/proto/android/view/remote_animation_target.proto
@@ -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.
+ */
+
+syntax = "proto2";
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.view;
+
+import "frameworks/base/core/proto/android/app/window_configuration.proto";
+import "frameworks/base/core/proto/android/graphics/point.proto";
+import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/view/surfacecontrol.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+/** Proto representation for RemoteAnimationTarget.java class. */
+message RemoteAnimationTargetProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional int32 task_id = 1;
+  optional int32 mode = 2;
+  optional .android.view.SurfaceControlProto leash = 3;
+  optional bool is_translucent = 4;
+  optional .android.graphics.RectProto clip_rect = 5;
+  optional .android.graphics.RectProto contentInsets = 6;
+  optional int32 prefix_order_index = 7;
+  optional .android.graphics.PointProto position = 8;
+  optional .android.graphics.RectProto source_container_bounds = 9;
+  optional .android.app.WindowConfigurationProto window_configuration = 10;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f521e4b..a4c0c54 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3089,7 +3089,8 @@
     <!-- Allows an application to collect usage infomation about brightness slider changes.
          <p>Not for use by third-party applications.</p>
          @hide
-         @SystemApi -->
+         @SystemApi
+         @TestApi -->
     <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
         android:protectionLevel="signature|privileged|development" />
 
@@ -3102,7 +3103,8 @@
 
     <!-- Allows an application to modify the display brightness configuration
          @hide
-         @SystemApi -->
+         @SystemApi
+         @TestApi -->
     <permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"
         android:protectionLevel="signature|privileged|development" />
 
diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml
index 6a0d902..a08f2d4 100644
--- a/core/res/res/drawable/ic_corp_user_badge.xml
+++ b/core/res/res/drawable/ic_corp_user_badge.xml
@@ -2,7 +2,8 @@
         android:width="36dp"
         android:height="36dp"
         android:viewportWidth="36.0"
-        android:viewportHeight="36.0">
+        android:viewportHeight="36.0"
+        android:tint="?attr/colorControlNormal">
     <path
         android:pathData="M16.3,11.3h3.4v1.7h-3.4z"
         android:fillColor="#FFFFFF"/>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc937e6..88dfe42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -148,16 +148,16 @@
     <string name="CLIRPermanent">You can\'t change the caller ID setting.</string>
 
     <!-- Notification title to tell the user that data service is blocked by access control. -->
-    <string name="RestrictedOnDataTitle">No data service</string>
+    <string name="RestrictedOnDataTitle">No mobile data service</string>
     <!-- Notification title to tell the user that emergency calling is blocked by access control. -->
-    <string name="RestrictedOnEmergencyTitle">No emergency calling</string>
+    <string name="RestrictedOnEmergencyTitle">Emergency calling unavailable</string>
     <!-- Notification title to tell the user that normal service is blocked by access control. -->
     <string name="RestrictedOnNormalTitle">No voice service</string>
     <!-- Notification title to tell the user that all emergency and normal voice services are blocked by access control. -->
-    <string name="RestrictedOnAllVoiceTitle">No voice/emergency service</string>
+    <string name="RestrictedOnAllVoiceTitle">No voice service or emergency calling</string>
 
     <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. -->
-    <string name="RestrictedStateContent">Temporarily not offered by the mobile network at your location</string>
+    <string name="RestrictedStateContent">Temporarily turned off by your carrier</string>
 
     <!-- Displayed to tell the user that they should switch their network preference. -->
     <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
@@ -352,9 +352,6 @@
     <!-- Work profile deleted notification--> <skip />
     <!-- Shows up in the notification's title when the system deletes the work profile. [CHAR LIMIT=NONE] -->
     <string name="work_profile_deleted">Work profile deleted</string>
-    <!-- Content text for a notification. The Title of the notification is "Work profile deleted".
-        This says that the profile is deleted by the system as a result of the current profile owner gone missing. [CHAR LIMIT=100]-->
-    <string name="work_profile_deleted_description">Work profile deleted due to missing admin app</string>
     <!-- Content text for an expanded notification. The Title of the notification is "Work profile deleted".
         This further explains that the profile is deleted by the system as a result of the current profile admin gone missing. [CHAR LIMIT=NONE]-->
     <string name="work_profile_deleted_details">The work profile admin app is either missing or corrupted.
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fdbcd8a..25b053b 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1318,6 +1318,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- volume background -->
+        <item name="panelColorBackground">@color/primary_material_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 53c22f6..0f019e71 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1379,6 +1379,10 @@
             android:theme="@style/Theme">
         </activity>
 
+        <activity android:name="android.app.activity.ActivityThreadTest$TestActivity"
+            android:exported="true">
+        </activity>
+
         <activity
             android:name="android.os.TestVrActivity"
             android:enableVrMode="com.android.frameworks.coretests/android.os.TestVrActivity$TestVrListenerService">
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 970a0f0..7b5ad9a 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -14,15 +14,16 @@
      limitations under the License.
 -->
 <configuration description="Runs Frameworks Core Tests.">
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="FrameworksCoreTests.apk" />
         <option name="test-file-name" value="BstatsTestApp.apk" />
     </target_preparer>
-
-    <option name="test-suite-tag" value="apct" />
     <option name="test-tag" value="FrameworksCoreTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.coretests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
     </test>
 </configuration>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
new file mode 100644
index 0000000..4628aa9
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.app.IApplicationThread;
+import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.MergedConfiguration;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for verifying {@link android.app.ActivityThread} class.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class ActivityThreadTest {
+
+    private final ActivityTestRule mActivityTestRule =
+            new ActivityTestRule(TestActivity.class, true /* initialTouchMode */,
+                    false /* launchActivity */);
+
+    @Test
+    public void testDoubleRelaunch() throws Exception {
+        final Activity activity = mActivityTestRule.launchActivity(new Intent());
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+
+        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
+        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
+    public void testResumeAfterRelaunch() throws Exception {
+        final Activity activity = mActivityTestRule.launchActivity(new Intent());
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+
+        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
+        appThread.scheduleTransaction(newResumeTransaction(activity));
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
+        final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
+                null, 0, new MergedConfiguration(),
+                false /* preserveWindow */);
+        final ResumeActivityItem resumeStateRequest =
+                ResumeActivityItem.obtain(true /* isForward */);
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+        final ClientTransaction transaction =
+                ClientTransaction.obtain(appThread, activity.getActivityToken());
+        transaction.addCallback(callbackItem);
+        transaction.setLifecycleStateRequest(resumeStateRequest);
+
+        return transaction;
+    }
+
+    private static ClientTransaction newResumeTransaction(Activity activity) {
+        final ResumeActivityItem resumeStateRequest =
+                ResumeActivityItem.obtain(true /* isForward */);
+        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+        final ClientTransaction transaction =
+                ClientTransaction.obtain(appThread, activity.getActivityToken());
+        transaction.setLifecycleStateRequest(resumeStateRequest);
+
+        return transaction;
+    }
+
+    // Test activity
+    public static class TestActivity extends Activity {
+    }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
index dece9a4..bcf9490 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java
@@ -16,7 +16,7 @@
 
 package com.android.multidexlegacyandexception;
 
-import android.support.multidex.MultiDexApplication;
+import androidx.multidex.MultiDexApplication;
 
 public class TestApplication extends MultiDexApplication {
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java
index 758ac1d..92a3b0c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/tests/MultiDexAndroidJUnitRunner.java
@@ -17,7 +17,7 @@
 package com.android.multidexlegacyandexception.tests;
 
 import android.os.Bundle;
-import android.support.multidex.MultiDex;
+import androidx.multidex.MultiDex;
 import android.support.test.runner.AndroidJUnitRunner;
 
 public class MultiDexAndroidJUnitRunner extends AndroidJUnitRunner {
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java
index c52ad29..f89d132 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java
@@ -16,7 +16,7 @@
 
 package com.android.multidexlegacytestapp;
 
-import android.support.multidex.MultiDexApplication;
+import androidx.multidex.MultiDexApplication;
 
 import java.lang.annotation.Annotation;
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java
index 963f904..9e41a92 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/src/com/android/multidexlegacytestapp/test2/MultiDexAndroidJUnitRunner.java
@@ -1,7 +1,7 @@
 package com.android.multidexlegacytestapp.test2;
 
 import android.os.Bundle;
-import android.support.multidex.MultiDex;
+import androidx.multidex.MultiDex;
 import android.support.test.runner.AndroidJUnitRunner;
 
 public class MultiDexAndroidJUnitRunner extends AndroidJUnitRunner {
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
index 7993c6f..5f006fe 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
@@ -9,7 +9,7 @@
         android:targetSdkVersion="18" />
 
     <application
-        android:name="android.support.multidex.MultiDexApplication"
+        android:name="androidx.multidex.MultiDexApplication"
         android:allowBackup="true"
         android:label="MultiDexLegacyTestApp_corrupted">
         <activity
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
index cb0a591..bbb4944 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
-import android.support.multidex.MultiDex;
+import androidx.multidex.MultiDex;
 import android.util.Log;
 
 import java.io.File;
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
index c7b066d..f3f11b9 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
@@ -9,7 +9,7 @@
         android:targetSdkVersion="18" />
 
     <application
-        android:name="android.support.multidex.MultiDexApplication"
+        android:name="androidx.multidex.MultiDexApplication"
         android:allowBackup="true"
         android:label="MultiDexLegacyVersionedTestApp_v1">
         <activity
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
index 4d24793..e43e56b 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
@@ -9,7 +9,7 @@
         android:targetSdkVersion="18" />
 
     <application
-        android:name="android.support.multidex.MultiDexApplication"
+        android:name="androidx.multidex.MultiDexApplication"
         android:allowBackup="true"
         android:label="MultiDexLegacyVersionedTestApp_v2">
         <activity
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
index 76c92dd..1d97228 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
@@ -9,7 +9,7 @@
         android:targetSdkVersion="18" />
 
     <application
-        android:name="android.support.multidex.MultiDexApplication"
+        android:name="androidx.multidex.MultiDexApplication"
         android:allowBackup="true"
         android:label="MultiDexLegacyVersionedTestApp_v3">
         <activity
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f27c11d..20c22b7 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -167,10 +167,8 @@
         nativeSetDefault(t.native_instance);
     }
 
-    // TODO: Make this public API. (b/64852739)
-    /** @hide */
-    @VisibleForTesting
-    public int getWeight() {
+    /** Returns the typeface's weight value */
+    public @IntRange(from = 0, to = 1000) int getWeight() {
         return mWeight;
     }
 
@@ -883,6 +881,15 @@
     }
 
     /**
+     * This method is used by supportlib-v27.
+     * TODO: Remove private API use in supportlib: http://b/72665240
+     */
+    private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
+                int italic) {
+        return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
+    }
+
+    /**
      * Create a new typeface from an array of font families, including
      * also the font families in the fallback list.
      * @param fallbackName the family name. If given families don't support characters, the
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 2a3967c..b51c662 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -156,17 +156,6 @@
                 @NonNull List<MediaItem2> playlist) { }
 
         /**
-         * Called when the playback state is changed.
-         *
-         * @param controller the controller for this event
-         * @param state latest playback state
-         * @hide
-         */
-        // TODO(jaewan): Remove (b/73971431)
-        public void onPlaybackStateChanged(@NonNull MediaController2 controller,
-                @NonNull PlaybackState2 state) { }
-
-        /**
          * Called when the player state is changed.
          *
          * @param controller the controller for this event
@@ -647,20 +636,6 @@
     }
 
     /**
-     * Get the lastly cached {@link PlaybackState2} from
-     * {@link ControllerCallback#onPlaybackStateChanged(MediaController2, PlaybackState2)}.
-     * <p>
-     * It may return {@code null} before the first callback or session has sent {@code null}
-     * playback state.
-     *
-     * @return a playback state. Can be {@code null}
-     * @hide
-     */
-    public @Nullable PlaybackState2 getPlaybackState() {
-        return mProvider.getPlaybackState_impl();
-    }
-
-    /**
      * Get the lastly cached player state from
      * {@link ControllerCallback#onPlayerStateChanged(MediaController2, int)}.
      *
@@ -748,7 +723,14 @@
     }
 
     /**
-     * Return playlist from the session.
+     * Returns the cached playlist from
+     * {@link ControllerCallback#onPlaylistChanged(MediaController2, MediaPlaylistAgent, List,
+     * MediaMetadata2)}.
+     * <p>
+     * This list may differ with the list that was specified with
+     * {@link #setPlaylist(List, MediaMetadata2)} depending on the {@link MediaPlaylistAgent}
+     * implementation. Use media items returned here for other playlist agent APIs such as
+     * {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
      *
      * @return playlist. Can be {@code null} if the controller doesn't have enough permission.
      */
@@ -758,12 +740,19 @@
 
     /**
      * Sets the playlist.
+     * <p>
+     * Even when the playlist is successfully set, use the playlist returned from
+     * {@link #getPlaylist()} for playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
+     * Otherwise the session in the remote process can't distinguish between media items.
      *
      * @param list playlist
      * @param metadata metadata of the playlist
+     * @see #getPlaylist()
+     * @see ControllerCallback#onPlaylistChanged(
+     *      MediaController2, MediaPlaylistAgent, List, MediaMetadata2)
      */
     public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
-        // TODO(jaewan): Implement (b/74174649)
+        mProvider.setPlaylist_impl(list, metadata);
     }
 
     /**
@@ -772,17 +761,20 @@
      * @param metadata metadata of the playlist
      */
     public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
-        // TODO(jaewan): Implement (b/74174649)
+        mProvider.updatePlaylistMetadata_impl(metadata);
     }
 
     /**
-     * Returns the playlist metadata
+     * Gets the lastly cached playlist playlist metadata either from
+     * {@link ControllerCallback#onPlaylistMetadataChanged(
+     * MediaController2, MediaPlaylistAgent, MediaMetadata2)} or
+     * {@link ControllerCallback#onPlaylistChanged(
+     * MediaController2, MediaPlaylistAgent, List, MediaMetadata2)}.
      *
      * @return metadata metadata of the playlist, or null if none is set
      */
     public @Nullable MediaMetadata2 getPlaylistMetadata() {
-        // TODO(jaewan): Implement (b/74174649)
-        return null;
+        return mProvider.getPlaylistMetadata_impl();
     }
 
     /**
@@ -797,15 +789,14 @@
     }
 
     /**
-     * Inserts the media item to the play list at position index.
+     * Inserts the media item to the playlist at position index.
      * <p>
      * This will not change the currently playing media item.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
+     * If index is less than or equal to the current index of the playlist,
+     * the current index of the playlist will be incremented correspondingly.
      *
      * @param index the index you want to add
      * @param item the media item you want to add
-     * @throws IndexOutOfBoundsException if index is outside play list range
      */
     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.addPlaylistItem_impl(index, item);
@@ -816,6 +807,8 @@
      *<p>
      * If the item is the currently playing item of the playlist, current playback
      * will be stopped and playback moves to next source in the list.
+     *
+     * @param item the media item you want to add
      */
     public void removePlaylistItem(@NonNull MediaItem2 item) {
         mProvider.removePlaylistItem_impl(item);
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 2db1392..b50c3e4 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -65,6 +65,13 @@
     }
 
     /**
+     * @hide
+     */
+    public MediaItem2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
      * Return this object as a bundle to share between processes.
      *
      * @return a new bundle instance
@@ -141,9 +148,7 @@
 
     @Override
     public boolean equals(Object obj) {
-        // TODO(jaewan): Override this. MediaItem2 may have auto-generated srcId when the DSD isn't
-        //               set, and it should be compared for the equals.
-        return super.equals(obj);
+        return mProvider.equals_impl(obj);
     }
 
     /**
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 745eb74d..1aeed6d 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -17,6 +17,8 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -30,7 +32,7 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -367,27 +369,79 @@
 
     private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
 
+    public static final class BitmapParams {
+        private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
+        private Bitmap.Config outActualConfig = Bitmap.Config.ARGB_8888;
+
+        /**
+         * Create a default BitmapParams object. By default, it uses {@link Bitmap.Config#ARGB_8888}
+         * as the preferred bitmap config.
+         */
+        public BitmapParams() {}
+
+        /**
+         * Set the preferred bitmap config for the decoder to decode into.
+         *
+         * If not set, or the request cannot be met, the decoder will output
+         * in {@link Bitmap.Config#ARGB_8888} config by default.
+         *
+         * After decode, the actual config used can be retrieved by {@link #getActualConfig()}.
+         *
+         * @param config the preferred bitmap config to use.
+         */
+        public void setPreferredConfig(@NonNull Bitmap.Config config) {
+            if (config == null) {
+                throw new IllegalArgumentException("preferred config can't be null");
+            }
+            inPreferredConfig = config;
+        }
+
+        /**
+         * Retrieve the preferred bitmap config in the params.
+         *
+         * @return the preferred bitmap config.
+         */
+        public @NonNull Bitmap.Config getPreferredConfig() {
+            return inPreferredConfig;
+        }
+
+        /**
+         * Get the actual bitmap config used to decode the bitmap after the decoding.
+         *
+         * @return the actual bitmap config used.
+         */
+        public @NonNull Bitmap.Config getActualConfig() {
+            return outActualConfig;
+        }
+    }
+
     /**
      * This method retrieves a video frame by its index. It should only be called
      * after {@link #setDataSource}.
      *
+     * After the bitmap is returned, you can query the actual parameters that were
+     * used to create the bitmap from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
+     *
      * @param frameIndex 0-based index of the video frame. The frame index must be that of
      *        a valid frame. The total number of frames available for retrieval can be queried
      *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain video or image sequences.
      * @throws IllegalArgumentException if the requested frame index does not exist.
      *
      * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
      *
-     * @see #getFramesAtIndex(int, int)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
      */
-    public Bitmap getFrameAtIndex(int frameIndex) {
-        Bitmap[] bitmaps = getFramesAtIndex(frameIndex, 1);
-        if (bitmaps == null || bitmaps.length < 1) {
+    public Bitmap getFrameAtIndex(int frameIndex, @Nullable BitmapParams params) {
+        List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params);
+        if (bitmaps == null || bitmaps.size() < 1) {
             return null;
         }
-        return bitmaps[0];
+        return bitmaps.get(0);
     }
 
     /**
@@ -395,24 +449,31 @@
      * specified index. It should only be called after {@link #setDataSource}.
      *
      * If the caller intends to retrieve more than one consecutive video frames,
-     * this method is preferred over {@link #getFrameAtIndex(int)} for efficiency.
+     * this method is preferred over {@link #getFrameAtIndex(int, BitmapParams)} for efficiency.
+     *
+     * After the bitmaps are returned, you can query the actual parameters that were
+     * used to create the bitmaps from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmaps with {@link BitmapParams#getActualConfig}.
      *
      * @param frameIndex 0-based index of the first video frame to retrieve. The frame index
      *        must be that of a valid frame. The total number of frames available for retrieval
      *        can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
      * @param numFrames number of consecutive video frames to retrieve. Must be a positive
      *        value. The stream must contain at least numFrames frames starting at frameIndex.
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *        If null, default config will be chosen.
      *
      * @throws IllegalStateException if the container doesn't contain video or image sequences.
      * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
      *         stream doesn't contain at least numFrames starting at frameIndex.
 
-     * @return An array of Bitmaps containing the requested video frames. The returned
+     * @return An list of Bitmaps containing the requested video frames. The returned
      *         array could contain less frames than requested if the retrieval fails.
      *
-     * @see #getFrameAtIndex(int)
+     * @see #getFrameAtIndex(int, BitmapParams)
      */
-    public Bitmap[] getFramesAtIndex(int frameIndex, int numFrames) {
+    public List<Bitmap> getFramesAtIndex(
+            int frameIndex, int numFrames, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
             throw new IllegalStateException("Does not contail video or image sequences");
         }
@@ -424,24 +485,32 @@
             throw new IllegalArgumentException("Invalid frameIndex or numFrames: "
                 + frameIndex + ", " + numFrames);
         }
-        return _getFrameAtIndex(frameIndex, numFrames);
+        return _getFrameAtIndex(frameIndex, numFrames, params);
     }
-    private native Bitmap[] _getFrameAtIndex(int frameIndex, int numFrames);
+    private native List<Bitmap> _getFrameAtIndex(
+            int frameIndex, int numFrames, @Nullable BitmapParams params);
 
     /**
      * This method retrieves a still image by its index. It should only be called
      * after {@link #setDataSource}.
      *
+     * After the bitmap is returned, you can query the actual parameters that were
+     * used to create the bitmap from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
+     *
      * @param imageIndex 0-based index of the image, with negative value indicating
      *        the primary image.
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *        If null, default config will be chosen.
+     *
      * @throws IllegalStateException if the container doesn't contain still images.
      * @throws IllegalArgumentException if the requested image does not exist.
      *
      * @return the requested still image, or null if the image cannot be retrieved.
      *
-     * @see #getPrimaryImage
+     * @see #getPrimaryImage(BitmapParams)
      */
-    public Bitmap getImageAtIndex(int imageIndex) {
+    public Bitmap getImageAtIndex(int imageIndex, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
             throw new IllegalStateException("Does not contail still images");
         }
@@ -451,24 +520,31 @@
             throw new IllegalArgumentException("Invalid image index: " + imageCount);
         }
 
-        return _getImageAtIndex(imageIndex);
+        return _getImageAtIndex(imageIndex, params);
     }
 
     /**
      * This method retrieves the primary image of the media content. It should only
      * be called after {@link #setDataSource}.
      *
+     * After the bitmap is returned, you can query the actual parameters that were
+     * used to create the bitmap from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
+     *
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *        If null, default config will be chosen.
+     *
      * @return the primary image, or null if it cannot be retrieved.
      *
      * @throws IllegalStateException if the container doesn't contain still images.
      *
-     * @see #getImageAtIndex(int)
+     * @see #getImageAtIndex(int, BitmapParams)
      */
-    public Bitmap getPrimaryImage() {
-        return getImageAtIndex(-1);
+    public Bitmap getPrimaryImage(@Nullable BitmapParams params) {
+        return getImageAtIndex(-1, params);
     }
 
-    private native Bitmap _getImageAtIndex(int imageIndex);
+    private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params);
 
     /**
      * Call this method after setDataSource(). This method finds the optional
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 08defbb..ece19b9 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -214,7 +214,8 @@
  *         call returns right away, the actual seek operation may take a while to
  *         finish, especially for audio/video being streamed. When the actual
  *         seek operation completes, the internal player engine calls a user
- *         supplied MediaPlayer2EventCallback.onCallComplete() with {@link #MEDIA_CALL_SEEK_TO}
+ *         supplied MediaPlayer2EventCallback.onCallCompleted() with
+ *         {@link #CALL_COMPLETED_SEEK_TO}
  *         if an MediaPlayer2EventCallback has been registered beforehand via
  *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
  *         <li>Please
@@ -819,7 +820,7 @@
      * of a batch of commands.
      */
     // This is an asynchronous call.
-    public void notifyWhenCommandLabelReached(Object label) { }
+    public void notifyWhenCommandLabelReached(@NonNull Object label) { }
 
     /**
      * Sets the {@link SurfaceHolder} to use for displaying the video
@@ -1051,6 +1052,7 @@
     /**
      * MediaPlayer2 has not been prepared or just has been reset.
      * In this state, MediaPlayer2 doesn't fetch data.
+     * @hide
      */
     public static final int MEDIAPLAYER2_STATE_IDLE = 1;
 
@@ -1058,29 +1060,33 @@
      * MediaPlayer2 has been just prepared.
      * In this state, MediaPlayer2 just fetches data from media source,
      * but doesn't actively render data.
+     * @hide
      */
     public static final int MEDIAPLAYER2_STATE_PREPARED = 2;
 
     /**
      * MediaPlayer2 is paused.
      * In this state, MediaPlayer2 doesn't actively render data.
+     * @hide
      */
     public static final int MEDIAPLAYER2_STATE_PAUSED = 3;
 
     /**
      * MediaPlayer2 is actively playing back data.
+     * @hide
      */
     public static final int MEDIAPLAYER2_STATE_PLAYING = 4;
 
     /**
      * MediaPlayer2 has hit some fatal error and cannot continue playback.
+     * @hide
      */
     public static final int MEDIAPLAYER2_STATE_ERROR = 5;
 
     /**
      * @hide
      */
-    @IntDef({
+    @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
         MEDIAPLAYER2_STATE_IDLE,
         MEDIAPLAYER2_STATE_PREPARED,
         MEDIAPLAYER2_STATE_PAUSED,
@@ -1093,6 +1099,7 @@
      * Gets the current MediaPlayer2 state.
      *
      * @return the current MediaPlayer2 state.
+     * @hide
      */
     public abstract @MediaPlayer2State int getMediaPlayer2State();
 
@@ -1170,8 +1177,7 @@
     public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
 
     /** @hide */
-    @IntDef(
-        value = {
+    @IntDef(flag = false, prefix = "PLAYBACK_RATE_AUDIO_MODE", value = {
             PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
             PLAYBACK_RATE_AUDIO_MODE_STRETCH,
             PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
@@ -1276,8 +1282,7 @@
     public static final int SEEK_CLOSEST          = 0x03;
 
     /** @hide */
-    @IntDef(
-        value = {
+    @IntDef(flag = false, prefix = "SEEK", value = {
             SEEK_PREVIOUS_SYNC,
             SEEK_NEXT_SYNC,
             SEEK_CLOSEST_SYNC,
@@ -1302,16 +1307,6 @@
      * If msec is negative, time position zero will be used.
      * If msec is larger than duration, duration will be used.
      * @param mode the mode indicating where exactly to seek to.
-     * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp earlier than or the same as msec. Use
-     * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp later than or the same as msec. Use
-     * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp closest to or the same as msec. Use
-     * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may
-     * or may not be a sync frame but is closest to or the same as msec.
-     * {@link #SEEK_CLOSEST} often has larger performance overhead compared
-     * to the other options if there is no sync frame located at msec.
      */
     // This is an asynchronous call.
     public abstract void seekTo(long msec, @SeekMode int mode);
@@ -1753,28 +1748,20 @@
          * @param dsd the DataSourceDesc of this data source
          * @param data the timed metadata sample associated with this event
          */
-        public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { }
+        public void onTimedMetaDataAvailable(
+                MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { }
 
         /**
          * Called to indicate an error.
          *
          * @param mp the MediaPlayer2 the error pertains to
          * @param dsd the DataSourceDesc of this data source
-         * @param what the type of error that has occurred:
-         * <ul>
-         * <li>{@link #MEDIA_ERROR_UNKNOWN}
-         * </ul>
+         * @param what the type of error that has occurred.
          * @param extra an extra code, specific to the error. Typically
          * implementation dependent.
-         * <ul>
-         * <li>{@link #MEDIA_ERROR_IO}
-         * <li>{@link #MEDIA_ERROR_MALFORMED}
-         * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
-         * <li>{@link #MEDIA_ERROR_TIMED_OUT}
-         * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error.
-         * </ul>
          */
-        public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) { }
+        public void onError(
+                MediaPlayer2 mp, DataSourceDesc dsd, @MediaError int what, int extra) { }
 
         /**
          * Called to indicate an info or a warning.
@@ -1782,29 +1769,10 @@
          * @param mp the MediaPlayer2 the info pertains to.
          * @param dsd the DataSourceDesc of this data source
          * @param what the type of info or warning.
-         * <ul>
-         * <li>{@link #MEDIA_INFO_UNKNOWN}
-         * <li>{@link #MEDIA_INFO_STARTED_AS_NEXT}
-         * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
-         * <li>{@link #MEDIA_INFO_AUDIO_RENDERING_START}
-         * <li>{@link #MEDIA_INFO_PLAYBACK_COMPLETE}
-         * <li>{@link #MEDIA_INFO_PLAYLIST_END}
-         * <li>{@link #MEDIA_INFO_PREPARED}
-         * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
-         * <li>{@link #MEDIA_INFO_BUFFERING_START}
-         * <li>{@link #MEDIA_INFO_BUFFERING_END}
-         * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> -
-         *     bandwidth information is available (as <code>extra</code> kbps)
-         * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
-         * <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
-         * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
-         * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE}
-         * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT}
-         * </ul>
          * @param extra an extra code, specific to the info. Typically
          * implementation dependent.
          */
-        public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) { }
+        public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, @MediaInfo int what, int extra) { }
 
         /**
          * Called to acknowledge an API call.
@@ -1812,33 +1780,11 @@
          * @param mp the MediaPlayer2 the call was made on.
          * @param dsd the DataSourceDesc of this data source
          * @param what the enum for the API call.
-         * <ul>
-         * <li>{@link #MEDIA_CALL_ATTACH_AUX_EFFECT}
-         * <li>{@link #MEDIA_CALL_DESELECT_TRACK}
-         * <li>{@link #MEDIA_CALL_LOOP_CURRENT}
-         * <li>{@link #MEDIA_CALL_PAUSE}
-         * <li>{@link #MEDIA_CALL_PLAY}
-         * <li>{@link #MEDIA_CALL_PREPARE}
-         * <li>{@link #MEDIA_CALL_RELEASE_DRM}
-         * <li>{@link #MEDIA_CALL_RESTORE_DRM_KEYS}
-         * <li>{@link #MEDIA_CALL_SEEK_TO}
-         * <li>{@link #MEDIA_CALL_SELECT_TRACK}
-         * <li>{@link #MEDIA_CALL_SET_AUDIO_ATTRIBUTES}
-         * <li>{@link #MEDIA_CALL_SET_AUDIO_SESSION_ID}
-         * <li>{@link #MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL}
-         * <li>{@link #MEDIA_CALL_SET_DATA_SOURCE}
-         * <li>{@link #MEDIA_CALL_SET_NEXT_DATA_SOURCE}
-         * <li>{@link #MEDIA_CALL_SET_NEXT_DATA_SOURCES}
-         * <li>{@link #MEDIA_CALL_SET_PLAYBACK_PARAMS}
-         * <li>{@link #MEDIA_CALL_SET_PLAYBACK_SPEED}
-         * <li>{@link #MEDIA_CALL_SET_PLAYER_VOLUME}
-         * <li>{@link #MEDIA_CALL_SET_SURFACE}
-         * <li>{@link #MEDIA_CALL_SET_SYNC_PARAMS}
-         * <li>{@link #MEDIA_CALL_SKIP_TO_NEXT}
-         * </ul>
          * @param status the returned status code for the call.
          */
-        public void onCallComplete(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) { }
+        public void onCallCompleted(
+                MediaPlayer2 mp, DataSourceDesc dsd, @CallCompleted int what,
+                @CallStatus int status) { }
 
         /**
          * Called to indicate media clock has changed.
@@ -1847,7 +1793,8 @@
          * @param dsd the DataSourceDesc of this data source
          * @param timestamp the new media clock.
          */
-        public void onMediaTimeChanged(MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
+        public void onMediaTimeChanged(
+                MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
 
         /**
          * Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed.
@@ -1856,7 +1803,7 @@
          * @param label the application specific Object given by
          *        {@link #notifyWhenCommandLabelReached(Object)}.
          */
-        public void onCommandLabelReached(MediaPlayer2 mp, Object label) { }
+        public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { }
     }
 
     /**
@@ -1929,6 +1876,20 @@
      */
     public static final int MEDIA_ERROR_SYSTEM = -2147483648;
 
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "MEDIA_ERROR", value = {
+            MEDIA_ERROR_UNKNOWN,
+            MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,
+            MEDIA_ERROR_IO,
+            MEDIA_ERROR_MALFORMED,
+            MEDIA_ERROR_UNSUPPORTED,
+            MEDIA_ERROR_TIMED_OUT,
+            MEDIA_ERROR_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaError {}
 
     /* Do not change these values without updating their counterparts
      * in include/media/mediaplayer2.h!
@@ -2059,129 +2020,246 @@
      */
     public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
 
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "MEDIA_INFO", value = {
+            MEDIA_INFO_UNKNOWN,
+            MEDIA_INFO_STARTED_AS_NEXT,
+            MEDIA_INFO_VIDEO_RENDERING_START,
+            MEDIA_INFO_AUDIO_RENDERING_START,
+            MEDIA_INFO_PLAYBACK_COMPLETE,
+            MEDIA_INFO_PLAYLIST_END,
+            MEDIA_INFO_PREPARED,
+            MEDIA_INFO_VIDEO_TRACK_LAGGING,
+            MEDIA_INFO_BUFFERING_START,
+            MEDIA_INFO_BUFFERING_END,
+            MEDIA_INFO_NETWORK_BANDWIDTH,
+            MEDIA_INFO_BUFFERING_UPDATE,
+            MEDIA_INFO_BAD_INTERLEAVING,
+            MEDIA_INFO_NOT_SEEKABLE,
+            MEDIA_INFO_METADATA_UPDATE,
+            MEDIA_INFO_EXTERNAL_METADATA_UPDATE,
+            MEDIA_INFO_AUDIO_NOT_PLAYING,
+            MEDIA_INFO_VIDEO_NOT_PLAYING,
+            MEDIA_INFO_TIMED_TEXT_ERROR,
+            MEDIA_INFO_UNSUPPORTED_SUBTITLE,
+            MEDIA_INFO_SUBTITLE_TIMED_OUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaInfo {}
+
     //--------------------------------------------------------------------------
     /** The player just completed a call {@link #attachAuxEffect}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_ATTACH_AUX_EFFECT = 1;
+    public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1;
 
     /** The player just completed a call {@link #deselectTrack}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_DESELECT_TRACK = 2;
+    public static final int CALL_COMPLETED_DESELECT_TRACK = 2;
 
     /** The player just completed a call {@link #loopCurrent}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_LOOP_CURRENT = 3;
+    public static final int CALL_COMPLETED_LOOP_CURRENT = 3;
 
     /** The player just completed a call {@link #pause}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_PAUSE = 4;
+    public static final int CALL_COMPLETED_PAUSE = 4;
 
     /** The player just completed a call {@link #play}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_PLAY = 5;
+    public static final int CALL_COMPLETED_PLAY = 5;
 
     /** The player just completed a call {@link #prepare}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_PREPARE = 6;
+    public static final int CALL_COMPLETED_PREPARE = 6;
 
     /** The player just completed a call {@link #releaseDrm}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_RELEASE_DRM = 12;
+    public static final int CALL_COMPLETED_RELEASE_DRM = 12;
 
     /** The player just completed a call {@link #restoreDrmKeys}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_RESTORE_DRM_KEYS = 13;
+    public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13;
 
     /** The player just completed a call {@link #seekTo}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SEEK_TO = 14;
+    public static final int CALL_COMPLETED_SEEK_TO = 14;
 
     /** The player just completed a call {@link #selectTrack}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SELECT_TRACK = 15;
+    public static final int CALL_COMPLETED_SELECT_TRACK = 15;
 
     /** The player just completed a call {@link #setAudioAttributes}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_AUDIO_ATTRIBUTES = 16;
+    public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16;
 
     /** The player just completed a call {@link #setAudioSessionId}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_AUDIO_SESSION_ID = 17;
+    public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17;
 
     /** The player just completed a call {@link #setAuxEffectSendLevel}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_AUX_EFFECT_SEND_LEVEL = 18;
+    public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18;
 
     /** The player just completed a call {@link #setDataSource}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_DATA_SOURCE = 19;
+    public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19;
 
     /** The player just completed a call {@link #setNextDataSource}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCE = 22;
+    public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22;
 
     /** The player just completed a call {@link #setNextDataSources}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_NEXT_DATA_SOURCES = 23;
+    public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23;
 
     /** The player just completed a call {@link #setPlaybackParams}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_PLAYBACK_PARAMS = 24;
+    public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24;
 
     /** The player just completed a call {@link #setPlaybackSpeed}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_PLAYBACK_SPEED = 25;
+    public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25;
 
     /** The player just completed a call {@link #setPlayerVolume}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_PLAYER_VOLUME = 26;
+    public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26;
 
     /** The player just completed a call {@link #setSurface}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_SURFACE = 27;
+    public static final int CALL_COMPLETED_SET_SURFACE = 27;
 
     /** The player just completed a call {@link #setSyncParams}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SET_SYNC_PARAMS = 28;
+    public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28;
 
     /** The player just completed a call {@link #skipToNext}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      */
-    public static final int MEDIA_CALL_SKIP_TO_NEXT = 29;
+    public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29;
 
     /** The player just completed a call {@link #setBufferingParams}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      * @hide
      */
-    public static final int MEDIA_CALL_SET_BUFFERING_PARAMS = 1001;
+    public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
 
-    /** The player just completed a call {@link #setPreferredDevice}.
-     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallComplete
+    /** The player just completed a call {@code setVideoScalingMode}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
      * @hide
      */
-    public static final int MEDIA_CALL_SET_PREFERRED_DEVICE = 1002;
+    public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
 
+    /** The player just completed a call {@code notifyWhenCommandLabelReached}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+     * @hide
+     */
+    public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "CALL_COMPLETED", value = {
+            CALL_COMPLETED_ATTACH_AUX_EFFECT,
+            CALL_COMPLETED_DESELECT_TRACK,
+            CALL_COMPLETED_LOOP_CURRENT,
+            CALL_COMPLETED_PAUSE,
+            CALL_COMPLETED_PLAY,
+            CALL_COMPLETED_PREPARE,
+            CALL_COMPLETED_RELEASE_DRM,
+            CALL_COMPLETED_RESTORE_DRM_KEYS,
+            CALL_COMPLETED_SEEK_TO,
+            CALL_COMPLETED_SELECT_TRACK,
+            CALL_COMPLETED_SET_AUDIO_ATTRIBUTES,
+            CALL_COMPLETED_SET_AUDIO_SESSION_ID,
+            CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL,
+            CALL_COMPLETED_SET_DATA_SOURCE,
+            CALL_COMPLETED_SET_NEXT_DATA_SOURCE,
+            CALL_COMPLETED_SET_NEXT_DATA_SOURCES,
+            CALL_COMPLETED_SET_PLAYBACK_PARAMS,
+            CALL_COMPLETED_SET_PLAYBACK_SPEED,
+            CALL_COMPLETED_SET_PLAYER_VOLUME,
+            CALL_COMPLETED_SET_SURFACE,
+            CALL_COMPLETED_SET_SYNC_PARAMS,
+            CALL_COMPLETED_SKIP_TO_NEXT,
+            CALL_COMPLETED_SET_BUFFERING_PARAMS,
+            CALL_COMPLETED_SET_VIDEO_SCALING_MODE,
+            CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallCompleted {}
+
+    /** Status code represents that call is completed without an error.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_NO_ERROR = 0;
+
+    /** Status code represents that call is ended with an unknown error.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_ERROR_UNKNOWN = Integer.MIN_VALUE;
+
+    /** Status code represents that the player is not in valid state for the operation.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_INVALID_OPERATION = 1;
+
+    /** Status code represents that the argument is illegal.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_BAD_VALUE = 2;
+
+    /** Status code represents that the operation is not allowed.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_PERMISSION_DENIED = 3;
+
+    /** Status code represents a file or network related operation error.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_ERROR_IO = 4;
+
+    /** Status code represents that DRM operation is called before preparing a DRM scheme through
+     *  {@link #prepareDrm}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_NO_DRM_SCHEME = 5;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "CALL_STATUS", value = {
+            CALL_STATUS_NO_ERROR,
+            CALL_STATUS_ERROR_UNKNOWN,
+            CALL_STATUS_INVALID_OPERATION,
+            CALL_STATUS_BAD_VALUE,
+            CALL_STATUS_PERMISSION_DENIED,
+            CALL_STATUS_ERROR_IO,
+            CALL_STATUS_NO_DRM_SCHEME})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallStatus {}
 
     // Modular DRM begin
 
@@ -2238,13 +2316,10 @@
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
          * @param dsd the DataSourceDesc of this data source
-         * @param status the result of DRM preparation which can be
-         * {@link #PREPARE_DRM_STATUS_SUCCESS},
-         * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR},
-         * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or
-         * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}.
+         * @param status the result of DRM preparation.
          */
-        public void onDrmPrepared(MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
+        public void onDrmPrepared(
+                MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
     }
 
     /**
@@ -2288,7 +2363,7 @@
 
 
     /** @hide */
-    @IntDef({
+    @IntDef(flag = false, prefix = "PREPARE_DRM_STATUS", value = {
         PREPARE_DRM_STATUS_SUCCESS,
         PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
         PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index ee1bb50..1c4d898 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -213,18 +213,13 @@
      */
     @Override
     public void play() {
-        synchronized (mTaskLock) {
-            mPendingTasks.add(new Task(MEDIA_CALL_PLAY, false) {
-                @Override
-                int process() {
-                    stayAwake(true);
-                    _start();
-                    // TODO: define public constants for return value (status).
-                    return 0;
-                }
-            });
-            processPendingTask_l();
-        }
+        addTask(new Task(CALL_COMPLETED_PLAY, false) {
+            @Override
+            void process() {
+                stayAwake(true);
+                _start();
+            }
+        });
     }
 
     private native void _start() throws IllegalStateException;
@@ -241,17 +236,12 @@
      */
     @Override
     public void prepare() {
-        synchronized (mTaskLock) {
-            mPendingTasks.add(new Task(MEDIA_CALL_PREPARE, true) {
-                @Override
-                int process() {
-                    _prepare();
-                    // TODO: define public constants for return value (status).
-                    return 0;
-                }
-            });
-            processPendingTask_l();
-        }
+        addTask(new Task(CALL_COMPLETED_PREPARE, true) {
+            @Override
+            void process() {
+                _prepare();
+            }
+        });
     }
 
     public native void _prepare();
@@ -264,8 +254,13 @@
      */
     @Override
     public void pause() {
-        stayAwake(false);
-        _pause();
+        addTask(new Task(CALL_COMPLETED_PAUSE, false) {
+            @Override
+            void process() {
+                stayAwake(false);
+                _pause();
+            }
+        });
     }
 
     private native void _pause() throws IllegalStateException;
@@ -277,7 +272,12 @@
      */
     @Override
     public void skipToNext() {
-        // TODO: switch to next data source and play
+        addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
+            @Override
+            void process() {
+                // TODO: switch to next data source and play
+            }
+        });
     }
 
     /**
@@ -357,14 +357,19 @@
      */
     @Override
     public void setAudioAttributes(@NonNull AudioAttributes attributes) {
-        if (attributes == null) {
-            final String msg = "Cannot set AudioAttributes to null";
-            throw new IllegalArgumentException(msg);
-        }
-        Parcel pattributes = Parcel.obtain();
-        attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
-        setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
-        pattributes.recycle();
+        addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
+            @Override
+            void process() {
+                if (attributes == null) {
+                    final String msg = "Cannot set AudioAttributes to null";
+                    throw new IllegalArgumentException(msg);
+                }
+                Parcel pattributes = Parcel.obtain();
+                attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
+                setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
+                pattributes.recycle();
+            }
+        });
     }
 
     @Override
@@ -384,16 +389,21 @@
      */
     @Override
     public void setDataSource(@NonNull DataSourceDesc dsd) {
-        Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-        // TODO: setDataSource could update exist data source
-        synchronized (mSrcLock) {
-            mCurrentDSD = dsd;
-            mCurrentSrcId = mSrcIdGenerator++;
-            try {
-                handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
-            } catch (IOException e) {
+        addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+                // TODO: setDataSource could update exist data source
+                synchronized (mSrcLock) {
+                    mCurrentDSD = dsd;
+                    mCurrentSrcId = mSrcIdGenerator++;
+                    try {
+                        handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
+                    } catch (IOException e) {
+                    }
+                }
             }
-        }
+        });
     }
 
     /**
@@ -406,21 +416,25 @@
      */
     @Override
     public void setNextDataSource(@NonNull DataSourceDesc dsd) {
-        Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-
-        synchronized (mSrcLock) {
-            mNextDSDs = new ArrayList<DataSourceDesc>(1);
-            mNextDSDs.add(dsd);
-            mNextSrcId = mSrcIdGenerator++;
-            mNextSourceState = NEXT_SOURCE_STATE_INIT;
-            mNextSourcePlayPending = false;
-        }
-        int state = getMediaPlayer2State();
-        if (state != MEDIAPLAYER2_STATE_IDLE) {
-            synchronized (mSrcLock) {
-                prepareNextDataSource_l();
+        addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+                synchronized (mSrcLock) {
+                    mNextDSDs = new ArrayList<DataSourceDesc>(1);
+                    mNextDSDs.add(dsd);
+                    mNextSrcId = mSrcIdGenerator++;
+                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mNextSourcePlayPending = false;
+                }
+                int state = getMediaPlayer2State();
+                if (state != MEDIAPLAYER2_STATE_IDLE) {
+                    synchronized (mSrcLock) {
+                        prepareNextDataSource_l();
+                    }
+                }
             }
-        }
+        });
     }
 
     /**
@@ -432,28 +446,33 @@
      */
     @Override
     public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
-        if (dsds == null || dsds.size() == 0) {
-            throw new IllegalArgumentException("data source list cannot be null or empty.");
-        }
-        for (DataSourceDesc dsd : dsds) {
-            if (dsd == null) {
-                throw new IllegalArgumentException(
-                        "DataSourceDesc in the source list cannot be null.");
-            }
-        }
+        addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
+            @Override
+            void process() {
+                if (dsds == null || dsds.size() == 0) {
+                    throw new IllegalArgumentException("data source list cannot be null or empty.");
+                }
+                for (DataSourceDesc dsd : dsds) {
+                    if (dsd == null) {
+                        throw new IllegalArgumentException(
+                                "DataSourceDesc in the source list cannot be null.");
+                    }
+                }
 
-        synchronized (mSrcLock) {
-            mNextDSDs = new ArrayList(dsds);
-            mNextSrcId = mSrcIdGenerator++;
-            mNextSourceState = NEXT_SOURCE_STATE_INIT;
-            mNextSourcePlayPending = false;
-        }
-        int state = getMediaPlayer2State();
-        if (state != MEDIAPLAYER2_STATE_IDLE) {
-            synchronized (mSrcLock) {
-                prepareNextDataSource_l();
+                synchronized (mSrcLock) {
+                    mNextDSDs = new ArrayList(dsds);
+                    mNextSrcId = mSrcIdGenerator++;
+                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mNextSourcePlayPending = false;
+                }
+                int state = getMediaPlayer2State();
+                if (state != MEDIAPLAYER2_STATE_IDLE) {
+                    synchronized (mSrcLock) {
+                        prepareNextDataSource_l();
+                    }
+                }
             }
-        }
+        });
     }
 
     @Override
@@ -469,8 +488,13 @@
      */
     @Override
     public void loopCurrent(boolean loop) {
-        // TODO: set the looping mode, send notification
-        setLooping(loop);
+        addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
+            @Override
+            void process() {
+                // TODO: set the looping mode, send notification
+                setLooping(loop);
+            }
+        });
     }
 
     private native void setLooping(boolean looping);
@@ -486,8 +510,12 @@
      */
     @Override
     public void setPlaybackSpeed(float speed) {
-        // TODO: send notification
-        setPlaybackParams(getPlaybackParams().setSpeed(speed));
+        addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_SPEED, false) {
+            @Override
+            void process() {
+                _setPlaybackParams(getPlaybackParams().setSpeed(speed));
+            }
+        });
     }
 
     /**
@@ -522,8 +550,12 @@
      */
     @Override
     public void setPlayerVolume(float volume) {
-        // send notification
-        _setVolume(volume, volume);
+        addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
+            @Override
+            void process() {
+                _setVolume(volume, volume);
+            }
+        });
     }
 
     private native void _setVolume(float leftVolume, float rightVolume);
@@ -630,7 +662,17 @@
 
     @Override
     public void notifyWhenCommandLabelReached(Object label) {
-        // TODO: create an entry in command queue
+        addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
+            @Override
+            void process() {
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onCommandLabelReached(
+                                MediaPlayer2Impl.this, label));
+                    }
+                }
+            }
+        });
     }
 
     /**
@@ -683,12 +725,17 @@
      */
     @Override
     public void setSurface(Surface surface) {
-        if (mScreenOnWhilePlaying && surface != null) {
-            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
-        }
-        mSurfaceHolder = null;
-        _setVideoSurface(surface);
-        updateSurfaceScreenOn();
+        addTask(new Task(CALL_COMPLETED_SET_SURFACE, false) {
+            @Override
+            void process() {
+                if (mScreenOnWhilePlaying && surface != null) {
+                    Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
+                }
+                mSurfaceHolder = null;
+                _setVideoSurface(surface);
+                updateSurfaceScreenOn();
+            }
+        });
     }
 
     /**
@@ -712,20 +759,25 @@
      */
     @Override
     public void setVideoScalingMode(int mode) {
-        if (!isVideoScalingModeSupported(mode)) {
-            final String msg = "Scaling mode " + mode + " is not supported";
-            throw new IllegalArgumentException(msg);
-        }
-        Parcel request = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        try {
-            request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
-            request.writeInt(mode);
-            invoke(request, reply);
-        } finally {
-            request.recycle();
-            reply.recycle();
-        }
+        addTask(new Task(CALL_COMPLETED_SET_VIDEO_SCALING_MODE, false) {
+            @Override
+            void process() {
+                if (!isVideoScalingModeSupported(mode)) {
+                    final String msg = "Scaling mode " + mode + " is not supported";
+                    throw new IllegalArgumentException(msg);
+                }
+                Parcel request = Parcel.obtain();
+                Parcel reply = Parcel.obtain();
+                try {
+                    request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
+                    request.writeInt(mode);
+                    invoke(request, reply);
+                } finally {
+                    request.recycle();
+                    reply.recycle();
+                }
+            }
+        });
     }
 
     /**
@@ -735,6 +787,13 @@
     public void clearPendingCommands() {
     }
 
+    private void addTask(Task task) {
+        synchronized (mTaskLock) {
+            mPendingTasks.add(task);
+            processPendingTask_l();
+        }
+    }
+
     @GuardedBy("mTaskLock")
     private void processPendingTask_l() {
         if (mCurrentTask != null) {
@@ -1332,7 +1391,17 @@
      * @hide
      */
     @Override
-    public native void setBufferingParams(@NonNull BufferingParams params);
+    public void setBufferingParams(@NonNull BufferingParams params) {
+        addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(params, "the BufferingParams cannot be null");
+                _setBufferingParams(params);
+            }
+        });
+    }
+
+    private native void _setBufferingParams(@NonNull BufferingParams params);
 
     /**
      * Sets playback rate and audio mode.
@@ -1386,7 +1455,17 @@
      * @throws IllegalArgumentException if params is not supported.
      */
     @Override
-    public native void setPlaybackParams(@NonNull PlaybackParams params);
+    public void setPlaybackParams(@NonNull PlaybackParams params) {
+        addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(params, "the PlaybackParams cannot be null");
+                _setPlaybackParams(params);
+            }
+        });
+    }
+
+    private native void _setPlaybackParams(@NonNull PlaybackParams params);
 
     /**
      * Gets the playback params, containing the current playback rate.
@@ -1409,7 +1488,17 @@
      * @throws IllegalArgumentException if params are not supported.
      */
     @Override
-    public native void setSyncParams(@NonNull SyncParams params);
+    public void setSyncParams(@NonNull SyncParams params) {
+        addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(params, "the SyncParams cannot be null");
+                _setSyncParams(params);
+            }
+        });
+    }
+
+    private native void _setSyncParams(@NonNull SyncParams params);
 
     /**
      * Gets the A/V sync mode.
@@ -1454,20 +1543,28 @@
      * @throws IllegalArgumentException if the mode is invalid.
      */
     @Override
-    public void seekTo(long msec, @SeekMode int mode) {
-        if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
-            final String msg = "Illegal seek mode: " + mode;
-            throw new IllegalArgumentException(msg);
-        }
-        // TODO: pass long to native, instead of truncating here.
-        if (msec > Integer.MAX_VALUE) {
-            Log.w(TAG, "seekTo offset " + msec + " is too large, cap to " + Integer.MAX_VALUE);
-            msec = Integer.MAX_VALUE;
-        } else if (msec < Integer.MIN_VALUE) {
-            Log.w(TAG, "seekTo offset " + msec + " is too small, cap to " + Integer.MIN_VALUE);
-            msec = Integer.MIN_VALUE;
-        }
-        _seekTo(msec, mode);
+    public void seekTo(final long msec, @SeekMode int mode) {
+        addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
+            @Override
+            void process() {
+                if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
+                    final String msg = "Illegal seek mode: " + mode;
+                    throw new IllegalArgumentException(msg);
+                }
+                // TODO: pass long to native, instead of truncating here.
+                long posMs = msec;
+                if (posMs > Integer.MAX_VALUE) {
+                    Log.w(TAG, "seekTo offset " + posMs + " is too large, cap to "
+                            + Integer.MAX_VALUE);
+                    posMs = Integer.MAX_VALUE;
+                } else if (posMs < Integer.MIN_VALUE) {
+                    Log.w(TAG, "seekTo offset " + posMs + " is too small, cap to "
+                            + Integer.MIN_VALUE);
+                    posMs = Integer.MIN_VALUE;
+                }
+                _seekTo(posMs, mode);
+            }
+        });
     }
 
     private native final void _seekTo(long msec, int mode);
@@ -1694,7 +1791,16 @@
      * @throws IllegalArgumentException if the sessionId is invalid.
      */
     @Override
-    public native void setAudioSessionId(int sessionId);
+    public void setAudioSessionId(int sessionId) {
+        addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
+            @Override
+            void process() {
+                _setAudioSessionId(sessionId);
+            }
+        });
+    }
+
+    private native void _setAudioSessionId(int sessionId);
 
     /**
      * Returns the audio session ID.
@@ -1720,7 +1826,16 @@
      * @param effectId system wide unique id of the effect to attach
      */
     @Override
-    public native void attachAuxEffect(int effectId);
+    public void attachAuxEffect(int effectId) {
+        addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
+            @Override
+            void process() {
+                _attachAuxEffect(effectId);
+            }
+        });
+    }
+
+    private native void _attachAuxEffect(int effectId);
 
     /**
      * Sets the send level of the player to the attached auxiliary effect.
@@ -1736,7 +1851,12 @@
      */
     @Override
     public void setAuxEffectSendLevel(float level) {
-        _setAuxEffectSendLevel(level);
+        addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
+            @Override
+            void process() {
+                _setAuxEffectSendLevel(level);
+            }
+        });
     }
 
     private native void _setAuxEffectSendLevel(float level);
@@ -2475,7 +2595,12 @@
      */
     @Override
     public void selectTrack(int index) {
-        selectOrDeselectTrack(index, true /* select */);
+        addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
+            @Override
+            void process() {
+                selectOrDeselectTrack(index, true /* select */);
+            }
+        });
     }
 
     /**
@@ -2494,7 +2619,12 @@
      */
     @Override
     public void deselectTrack(int index) {
-        selectOrDeselectTrack(index, false /* select */);
+        addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
+            @Override
+            void process() {
+                selectOrDeselectTrack(index, false /* select */);
+            }
+        });
     }
 
     private void selectOrDeselectTrack(int index, boolean select)
@@ -2697,8 +2827,11 @@
                     }
                 }
                 synchronized (mTaskLock) {
-                    if (mCurrentTask.mMediaCallType == MEDIA_CALL_PREPARE
+                    if (mCurrentTask != null
+                            && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
+                            && mCurrentTask.mDSD == dsd
                             && mCurrentTask.mNeedToWaitForEventToComplete) {
+                        mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
                         mCurrentTask = null;
                         processPendingTask_l();
                     }
@@ -2803,10 +2936,13 @@
 
             case MEDIA_SEEK_COMPLETE:
             {
-                synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                        cb.first.execute(() -> cb.second.onCallComplete(
-                                mMediaPlayer, mCurrentDSD, MEDIA_CALL_SEEK_TO, 0));
+                synchronized (mTaskLock) {
+                    if (mCurrentTask != null
+                            && mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
+                            && mCurrentTask.mNeedToWaitForEventToComplete) {
+                        mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
+                        mCurrentTask = null;
+                        processPendingTask_l();
                     }
                 }
             }
@@ -3390,32 +3526,39 @@
     public void releaseDrm()
             throws NoDrmSchemeException
     {
-        Log.v(TAG, "releaseDrm:");
+        addTask(new Task(CALL_COMPLETED_RELEASE_DRM, false) {
+            @Override
+            void process() throws NoDrmSchemeException {
+                synchronized (mDrmLock) {
+                    Log.v(TAG, "releaseDrm:");
 
-        synchronized (mDrmLock) {
-            if (!mActiveDrmScheme) {
-                Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
-                throw new NoDrmSchemeExceptionImpl("releaseDrm: No active DRM scheme to release.");
+                    if (!mActiveDrmScheme) {
+                        Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+                        throw new NoDrmSchemeExceptionImpl(
+                                "releaseDrm: No active DRM scheme to release.");
+                    }
+
+                    try {
+                        // we don't have the player's state in this layer. The below call raises
+                        // exception if we're in a non-stopped/prepared state.
+
+                        // for cleaning native/mediaserver crypto object
+                        _releaseDrm();
+
+                        // for cleaning client-side MediaDrm object; only called if above has succeeded
+                        cleanDrmObj();
+
+                        mActiveDrmScheme = false;
+                    } catch (IllegalStateException e) {
+                        Log.w(TAG, "releaseDrm: Exception ", e);
+                        throw new IllegalStateException(
+                                "releaseDrm: The player is not in a valid state.");
+                    } catch (Exception e) {
+                        Log.e(TAG, "releaseDrm: Exception ", e);
+                    }
+                }   // synchronized
             }
-
-            try {
-                // we don't have the player's state in this layer. The below call raises
-                // exception if we're in a non-stopped/prepared state.
-
-                // for cleaning native/mediaserver crypto object
-                _releaseDrm();
-
-                // for cleaning client-side MediaDrm object; only called if above has succeeded
-                cleanDrmObj();
-
-                mActiveDrmScheme = false;
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "releaseDrm: Exception ", e);
-                throw new IllegalStateException("releaseDrm: The player is not in a valid state.");
-            } catch (Exception e) {
-                Log.e(TAG, "releaseDrm: Exception ", e);
-            }
-        }   // synchronized
+        });
     }
 
 
@@ -3470,7 +3613,8 @@
         synchronized (mDrmLock) {
             if (!mActiveDrmScheme) {
                 Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("getDrmKeyRequest: Has to set a DRM scheme first.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "getDrmKeyRequest: Has to set a DRM scheme first.");
             }
 
             try {
@@ -3531,7 +3675,8 @@
 
             if (!mActiveDrmScheme) {
                 Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("getDrmKeyRequest: Has to set a DRM scheme first.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "getDrmKeyRequest: Has to set a DRM scheme first.");
             }
 
             try {
@@ -3541,8 +3686,8 @@
 
                 byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
 
-                Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response +
-                        " --> " + keySetResult);
+                Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response
+                        + " --> " + keySetResult);
 
 
                 return keySetResult;
@@ -3570,23 +3715,29 @@
     public void restoreDrmKeys(@NonNull byte[] keySetId)
             throws NoDrmSchemeException
     {
-        Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
+        addTask(new Task(CALL_COMPLETED_RESTORE_DRM_KEYS, false) {
+            @Override
+            void process() throws NoDrmSchemeException {
+                Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
 
-        synchronized (mDrmLock) {
+                synchronized (mDrmLock) {
 
-            if (!mActiveDrmScheme) {
-                Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("restoreDrmKeys: Has to set a DRM scheme first.");
+                    if (!mActiveDrmScheme) {
+                        Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+                        throw new NoDrmSchemeExceptionImpl(
+                                "restoreDrmKeys: Has to set a DRM scheme first.");
+                    }
+
+                    try {
+                        mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+                    } catch (Exception e) {
+                        Log.w(TAG, "restoreKeys Exception " + e);
+                        throw e;
+                    }
+
+                }   // synchronized
             }
-
-            try {
-                mDrmObj.restoreKeys(mDrmSessionId, keySetId);
-            } catch (Exception e) {
-                Log.w(TAG, "restoreKeys Exception " + e);
-                throw e;
-            }
-
-        }   // synchronized
+        });
     }
 
 
@@ -3611,7 +3762,8 @@
 
             if (!mActiveDrmScheme && !mDrmConfigAllowed) {
                 Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("getDrmPropertyString: Has to prepareDrm() first.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "getDrmPropertyString: Has to prepareDrm() first.");
             }
 
             try {
@@ -3649,7 +3801,8 @@
 
             if ( !mActiveDrmScheme && !mDrmConfigAllowed ) {
                 Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("setDrmPropertyString: Has to prepareDrm() first.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "setDrmPropertyString: Has to prepareDrm() first.");
             }
 
             try {
@@ -4587,34 +4740,61 @@
     private abstract class Task implements Runnable {
         private final int mMediaCallType;
         private final boolean mNeedToWaitForEventToComplete;
+        private DataSourceDesc mDSD;
 
         public Task (int mediaCallType, boolean needToWaitForEventToComplete) {
             mMediaCallType = mediaCallType;
             mNeedToWaitForEventToComplete = needToWaitForEventToComplete;
         }
 
-        abstract int process();
+        abstract void process() throws IOException, NoDrmSchemeException;
 
         @Override
         public void run() {
-            int status = process();
+            int status = CALL_STATUS_NO_ERROR;
+            try {
+                process();
+            } catch (IllegalStateException e) {
+                status = CALL_STATUS_INVALID_OPERATION;
+            } catch (IllegalArgumentException e) {
+                status = CALL_STATUS_BAD_VALUE;
+            } catch (SecurityException e) {
+                status = CALL_STATUS_PERMISSION_DENIED;
+            } catch (IOException e) {
+                status = CALL_STATUS_ERROR_IO;
+            } catch (NoDrmSchemeException e) {
+                status = CALL_STATUS_NO_DRM_SCHEME;
+            } catch (Exception e) {
+                status = CALL_STATUS_ERROR_UNKNOWN;
+            }
+            synchronized (mSrcLock) {
+                mDSD = mCurrentDSD;
+            }
 
-            if (!mNeedToWaitForEventToComplete) {
-                final DataSourceDesc dsd;
-                synchronized (mSrcLock) {
-                    dsd = mCurrentDSD;
-                }
-                synchronized (mEventCbLock) {
-                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
-                        cb.first.execute(() -> cb.second.onCallComplete(
-                                MediaPlayer2Impl.this, dsd, mMediaCallType, status));
-                    }
-                }
+            // TODO: Make native implementations asynchronous and let them send notifications.
+            if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
+
+                sendCompleteNotification(status);
+
                 synchronized (mTaskLock) {
                     mCurrentTask = null;
                     processPendingTask_l();
                 }
             }
         }
+
+        private void sendCompleteNotification(int status) {
+            // In {@link #notifyWhenCommandLabelReached} case, a separate callback
+            // {#link #onCommandLabelReached} is already called in {@code process()}.
+            if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
+                return;
+            }
+            synchronized (mEventCbLock) {
+                for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                    cb.first.execute(() -> cb.second.onCallCompleted(
+                            MediaPlayer2Impl.this, mDSD, mMediaCallType, status));
+                }
+            }
+        }
     };
 }
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 65378b4..e60c5f9 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.MediaPlayerBase.PLAYER_STATE_IDLE;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -61,7 +63,7 @@
  * sessions can be created to provide finer grain controls of media.
  * <p>
  * If you want to support background playback, {@link MediaSessionService2} is preferred
- * instead. With it, your playback can be revived even after you've finished playback. See
+ * instead. With it, your playback can be revived even after playback is finished. See
  * {@link MediaSessionService2} for details.
  * <p>
  * A session can be obtained by {@link Builder}. The owner of the session may pass its session token
@@ -254,7 +256,7 @@
     public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
 
     /**
-     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2).
+     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
      * <p>
      * Command would be sent directly to the playlist agent if the session doesn't reject the
      * request through the
@@ -263,11 +265,8 @@
     public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
 
     /**
-     * Command code for {@link MediaController2#getPlaylistMetadata()} ()}. This will expose
+     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
      * metadata information to the controller.
-     * *
-     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)} and
-     * {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
      * <p>
      * Command would be sent directly to the playlist agent if the session doesn't reject the
      * request through the
@@ -827,7 +826,11 @@
                 @NonNull MediaPlayerBase player, @NonNull MediaItem2 item, @BuffState int state) { }
 
         /**
-         * Called when a playlist is changed.
+         * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
+         * <p>
+         * This is called when the underlying agent has called
+         * {@link MediaPlaylistAgent.PlaylistEventCallback#onPlaylistChanged(MediaPlaylistAgent,
+         * List, MediaMetadata2)}.
          *
          * @param session the session for this event
          * @param playlistAgent playlist agent for this event
@@ -1638,13 +1641,36 @@
     }
 
     /**
-     * Return the {@link PlaybackState2} from the player.
+     * Get the player state.
      *
-     * @return playback state
+     * @return player state
      * @hide
      */
-    public PlaybackState2 getPlaybackState() {
-        return mProvider.getPlaybackState_impl();
+    public @PlayerState int getPlayerState() {
+        // TODO(jaewan): implement this (b/74578458)
+        return PLAYER_STATE_IDLE;
+    }
+
+    /**
+     * Get the current position.
+     *
+     * @return position
+     * @hide
+     */
+    public long getCurrentPosition() {
+        // TODO(jaewan): implement this (b/74578458)
+        return -1;
+    }
+
+    /**
+     * Get the buffered position.
+     *
+     * @return buffered position
+     * @hide
+     */
+    public long getBufferedPosition() {
+        // TODO(jaewan): implement this (b/74578458)
+        return -1;
     }
 
     /**
@@ -1681,7 +1707,7 @@
      * <p>
      * If it's not set, playback wouldn't happen for the item without data source descriptor.
      * <p>
-     * The helper will be run on the executor that you've specified by the
+     * The helper will be run on the executor that was specified by
      * {@link Builder#setSessionCallback(Executor, SessionCallback)}.
      *
      * @param helper a data source missing helper.
@@ -1705,40 +1731,46 @@
     }
 
     /**
-     * Return the playlist which is lastly set.
+     * Returns the playlist from the {@link MediaPlaylistAgent}.
+     * <p>
+     * This list may differ with the list that was specified with
+     * {@link #setPlaylist(List, MediaMetadata2)} depending on the {@link MediaPlaylistAgent}
+     * implementation. Use media items returned here for other playlist agent APIs such as
+     * {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
      *
      * @return playlist
+     * @see MediaPlaylistAgent#getPlaylist()
+     * @see SessionCallback#onPlaylistChanged(
+     *          MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
      */
     public List<MediaItem2> getPlaylist() {
         return mProvider.getPlaylist_impl();
     }
 
     /**
-     * Set a list of {@link MediaItem2} as the current play list.
-     *
-     * @param playlist A list of {@link MediaItem2} objects to set as a play list.
-     * @throws IllegalArgumentException if given {@param playlist} is null.
-     * @hide
-     */
-    // TODO(jaewan): Remove
-    public void setPlaylist(@NonNull List<MediaItem2> playlist) {
-        mProvider.setPlaylist_impl(playlist);
-    }
-
-    /**
-     * Set a list of {@link MediaItem2} as the current play list. Ensure uniqueness in the
-     * {@link MediaItem2} in the playlist so session can uniquely identity individual items.
+     * Sets a list of {@link MediaItem2} to the {@link MediaPlaylistAgent}. Ensure uniqueness of
+     * each {@link MediaItem2} in the playlist so the session can uniquely identity individual
+     * items.
      * <p>
-     * You may specify a {@link MediaItem2} without {@link DataSourceDesc}. However, in that case,
-     * you should set {@link OnDataSourceMissingHelper} for player to prepare.
+     * This may be an asynchronous call, and {@link MediaPlaylistAgent} may keep the copy of the
+     * list. Wait for {@link SessionCallback#onPlaylistChanged(MediaSession2, MediaPlaylistAgent,
+     * List, MediaMetadata2)} to know the operation finishes.
+     * <p>
+     * You may specify a {@link MediaItem2} without {@link DataSourceDesc}. In that case,
+     * {@link MediaPlaylistAgent} has responsibility to dynamically query {@link DataSourceDesc}
+     * when such media item is ready for preparation or play. Default implementation needs
+     * {@link OnDataSourceMissingHelper} for such case.
      *
      * @param list A list of {@link MediaItem2} objects to set as a play list.
-     * @throws IllegalArgumentException if given list is {@code null}, or has duplicated media item.
+     * @throws IllegalArgumentException if given list is {@code null}, or has duplicated media
+     * items.
+     * @see MediaPlaylistAgent#setPlaylist(List, MediaMetadata2)
+     * @see SessionCallback#onPlaylistChanged(
+     *          MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
      * @see #setOnDataSourceMissingHelper
      */
     public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
-        // TODO(jaewan): Handle metadata here (b/74174649)
-        // TODO(jaewan): Handle list change (b/74326040)
+        mProvider.setPlaylist_impl(list, metadata);
     }
 
     /**
@@ -1760,13 +1792,17 @@
         mProvider.skipToNextItem_impl();
     }
 
+    /**
+     * Gets the playlist metadata from the {@link MediaPlaylistAgent}.
+     *
+     * @return the playlist metadata
+     */
     public MediaMetadata2 getPlaylistMetadata() {
-        // TODO(jaewan): Implement (b/74174649)
-        return null;
+        return mProvider.getPlaylistMetadata_impl();
     }
 
     /**
-     * Add the media item to the play list at position index.
+     * Adds the media item to the playlist at position index.
      * <p>
      * This will not change the currently playing media item.
      * If index is less than or equal to the current index of the play list,
@@ -1774,26 +1810,25 @@
      *
      * @param index the index you want to add
      * @param item the media item you want to add
-     * @throws IndexOutOfBoundsException if index is outside play list range
      */
     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
         mProvider.addPlaylistItem_impl(index, item);
     }
 
     /**
-     * Remove the media item in the play list.
+     * Removes the media item in the playlist.
      * <p>
      * If the item is the currently playing item of the playlist, current playback
      * will be stopped and playback moves to next source in the list.
      *
-     * @throws IllegalArgumentException if the play list is null
+     * @param item the media item you want to add
      */
     public void removePlaylistItem(@NonNull MediaItem2 item) {
         mProvider.removePlaylistItem_impl(item);
     }
 
     /**
-     * Replace the media item at index in the playlist. This can be also used to update metadata of
+     * Replaces the media item at index in the playlist. This can be also used to update metadata of
      * an item.
      *
      * @param index the index of the item to replace
@@ -1813,8 +1848,13 @@
         return mProvider.getCurrentPlaylistItem_impl();
     }
 
+    /**
+     * Updates the playlist metadata to the {@link MediaPlaylistAgent}.
+     *
+     * @param metadata metadata of the playlist
+     */
     public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
-        // TODO(jaewan): Implement (b/74174649)
+        mProvider.updatePlaylistMetadata_impl(metadata);
     }
 
     public @RepeatMode int getRepeatMode() {
diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java
deleted file mode 100644
index afc2bfa..0000000
--- a/media/java/android/media/PlaybackState2.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.media.update.ApiLoader;
-import android.media.update.PlaybackState2Provider;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Playback state for a {@link MediaPlayerBase}, to be shared between {@link MediaSession2} and
- * {@link MediaController2}. This includes a playback state {@link #STATE_PLAYING},
- * the current playback position and extra.
- * @hide
- */
-// TODO(jaewan): Remove this (b/73971431)
-public final class PlaybackState2 {
-    // Similar to the PlaybackState with following changes
-    //    - Not implement Parcelable and added from/toBundle()
-    //    - Removed playback state that doesn't match with the MediaPlayer2
-    //      Full list should be finalized when the MediaPlayer2 has getter for the playback state.
-    //      Here's table for the MP2 state and PlaybackState2.State.
-    //         +----------------------------------------+----------------------------------------+
-    //         | MediaPlayer2 state                     | Matching PlaybackState2.State          |
-    //         | (Names are from MP2' Javadoc)          |                                        |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Idle: Just finished creating MP2       | STATE_NONE                             |
-    //         |     or reset() is called               |                                        |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Initialized: setDataSource/Playlist    | N/A (Session/Controller don't          |
-    //         |                                        |     differentiate with Prepared)       |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Prepared: Prepared after initialized   | STATE_PAUSED                           |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Started: Started playback              | STATE_PLAYING                          |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Paused: Paused playback                | STATE_PAUSED                           |
-    //         +----------------------------------------+----------------------------------------+
-    //         | PlaybackCompleted: Playback is done    | STATE_PAUSED                           |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Stopped: MP2.stop() is called.         | STATE_STOPPED                          |
-    //         |     prepare() is needed to play again  |                                        |
-    //         |     (Seemingly the same as initialized |                                        |
-    //         |     because cannot set data source     |                                        |
-    //         |     after this)                        |                                        |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Error: an API is called in a state     | STATE_ERROR                            |
-    //         |     that the API isn't supported       |                                        |
-    //         +----------------------------------------+----------------------------------------+
-    //         | End: MP2.close() is called to release  | N/A (MediaSession will be gone)        |
-    //         |    MP2. Cannot be reused anymore       |                                        |
-    //         +----------------------------------------+----------------------------------------+
-    //         | Started, but                           | STATE_BUFFERING                        |
-    //         |    EventCallback.onBufferingUpdate()   |                                        |
-    //         +----------------------------------------+----------------------------------------+
-    //    - Removed actions and custom actions.
-    //    - Removed error string
-    //    - Repeat mode / shuffle mode is now in the PlaylistParams
-    /**
-     * @hide
-     */
-    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_BUFFERING, STATE_ERROR})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface State {}
-
-    /**
-     * This is the default playback state and indicates that no media has been
-     * added yet, or the performer has been reset and has no content to play.
-     */
-    public final static int STATE_NONE = 0;
-
-    /**
-     * State indicating this item is currently stopped.
-     */
-    public final static int STATE_STOPPED = 1;
-
-    /**
-     * State indicating this item is currently paused.
-     */
-    public final static int STATE_PAUSED = 2;
-
-    /**
-     * State indicating this item is currently playing.
-     */
-    public final static int STATE_PLAYING = 3;
-
-    /**
-     * State indicating this item is currently buffering and will begin playing
-     * when enough data has buffered.
-     */
-    public final static int STATE_BUFFERING = 4;
-
-    /**
-     * State indicating this item is currently in an error state.
-     */
-    public final static int STATE_ERROR = 5;
-
-    /**
-     * Use this value for the position to indicate the position is not known.
-     */
-    public final static long PLAYBACK_POSITION_UNKNOWN = -1;
-
-    private final PlaybackState2Provider mProvider;
-
-    public PlaybackState2(@NonNull Context context, int state, long position, long updateTime,
-            float speed, long bufferedPosition, long activeItemId) {
-        mProvider = ApiLoader.getProvider(context).createPlaybackState2(context, this, state,
-                position, updateTime, speed, bufferedPosition, activeItemId);
-    }
-
-    @Override
-    public String toString() {
-        return mProvider.toString_impl();
-    }
-
-    /**
-     * Get the current state of playback. One of the following:
-     * <ul>
-     * <li> {@link PlaybackState2#STATE_NONE}</li>
-     * <li> {@link PlaybackState2#STATE_STOPPED}</li>
-     * <li> {@link PlaybackState2#STATE_PAUSED}</li>
-     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
-     * <li> {@link PlaybackState2#STATE_BUFFERING}</li>
-     * <li> {@link PlaybackState2#STATE_ERROR}</li>
-     * </ul>
-     */
-    @State
-    public int getState() {
-        return mProvider.getState_impl();
-    }
-
-    /**
-     * Get the current playback position in ms.
-     */
-    public long getPosition() {
-        return mProvider.getPosition_impl();
-    }
-
-    /**
-     * Get the current buffered position in ms. This is the farthest playback
-     * point that can be reached from the current position using only buffered
-     * content.
-     */
-    public long getBufferedPosition() {
-        return mProvider.getBufferedPosition_impl();
-    }
-
-    /**
-     * Get the current playback speed as a multiple of normal playback. This
-     * should be negative when rewinding. A value of 1 means normal playback and
-     * 0 means paused.
-     *
-     * @return The current speed of playback.
-     */
-    public float getPlaybackSpeed() {
-        return mProvider.getPlaybackSpeed_impl();
-    }
-
-    /**
-     * Get the elapsed real time at which position was last updated. If the
-     * position has never been set this will return 0;
-     *
-     * @return The last time the position was updated.
-     */
-    public long getLastPositionUpdateTime() {
-        return mProvider.getLastPositionUpdateTime_impl();
-    }
-
-    /**
-     * Get the id of the currently active item in the playlist.
-     *
-     * @return The id of the currently active item in the queue
-     */
-    public long getCurrentPlaylistItemIndex() {
-        return mProvider.getCurrentPlaylistItemIndex_impl();
-    }
-
-    /**
-     * Returns this object as a bundle to share between processes.
-     */
-    public @NonNull Bundle toBundle() {
-        return mProvider.toBundle_impl();
-    }
-
-    /**
-     * Creates an instance from a bundle which is previously created by {@link #toBundle()}.
-     *
-     * @param context context
-     * @param bundle A bundle created by {@link #toBundle()}.
-     * @return A new {@link PlaybackState2} instance. Returns {@code null} if the given
-     *         {@param bundle} is null, or if the {@param bundle} has no playback state parameters.
-     */
-    public @Nullable static PlaybackState2 fromBundle(@NonNull Context context,
-            @Nullable Bundle bundle) {
-        return ApiLoader.getProvider(context).fromBundle_PlaybackState2(context, bundle);
-    }
-}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 6b130cc..051321c 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -775,8 +775,7 @@
                         public void run() {
                             final Context context = mContext;
                             if (context != null) {
-                                ArrayList<MediaController> controllers
-                                        = new ArrayList<MediaController>();
+                                ArrayList<MediaController> controllers = new ArrayList<>();
                                 int size = tokens.size();
                                 for (int i = 0; i < size; i++) {
                                     controllers.add(new MediaController(context, tokens.get(i)));
@@ -814,10 +813,16 @@
         private final ISessionTokensListener.Stub mStub = new ISessionTokensListener.Stub() {
             @Override
             public void onSessionTokensChanged(final List<Bundle> bundles) {
-                mExecutor.execute(() -> {
-                    List<SessionToken2> tokens = toTokenList(mContext, bundles);
-                    mListener.onSessionTokensChanged(tokens);
-                });
+                final Executor executor = mExecutor;
+                if (executor != null) {
+                    executor.execute(() -> {
+                        final Context context = mContext;
+                        final OnSessionTokensChangedListener listener = mListener;
+                        if (context != null && listener != null) {
+                            listener.onSessionTokensChanged(toTokenList(context, bundles));
+                        }
+                    });
+                }
             }
         };
 
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 06e9544..55672b6 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -16,14 +16,13 @@
 
 package android.media.update;
 
-import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.media.AudioAttributes;
 import android.media.MediaController2.PlaybackInfo;
 import android.media.MediaItem2;
+import android.media.MediaMetadata2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.PlaylistParams;
-import android.media.PlaybackState2;
 import android.media.Rating2;
 import android.media.SessionToken2;
 import android.net.Uri;
@@ -58,6 +57,9 @@
     void setRating_impl(String mediaId, Rating2 rating);
     void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb);
     List<MediaItem2> getPlaylist_impl();
+    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
+    MediaMetadata2 getPlaylistMetadata_impl();
+    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
 
     void addPlaylistItem_impl(int index, MediaItem2 item);
     void replacePlaylistItem_impl(int index, MediaItem2 item);
@@ -65,7 +67,6 @@
 
     PlaylistParams getPlaylistParams_impl();
     void setPlaylistParams_impl(PlaylistParams params);
-    PlaybackState2 getPlaybackState_impl();
     int getPlayerState_impl();
     long getPosition_impl();
     float getPlaybackSpeed_impl();
diff --git a/media/java/android/media/update/MediaItem2Provider.java b/media/java/android/media/update/MediaItem2Provider.java
index b494f9e..47db22f 100644
--- a/media/java/android/media/update/MediaItem2Provider.java
+++ b/media/java/android/media/update/MediaItem2Provider.java
@@ -35,6 +35,7 @@
     MediaMetadata2 getMetadata_impl();
     String getMediaId_impl();
     DataSourceDesc getDataSourceDesc_impl();
+    boolean equals_impl(Object obj);
 
     interface BuilderProvider {
         Builder setMediaId_impl(String mediaId);
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 142650a..84ea369 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -47,6 +47,8 @@
     void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
             VolumeProvider2 volumeProvider);
     MediaPlayerBase getPlayer_impl();
+    MediaMetadata2 getPlaylistMetadata_impl();
+    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
     MediaPlaylistAgent getPlaylistAgent_impl();
     VolumeProvider2 getVolumeProvider_impl();
     SessionToken2 getToken_impl();
@@ -57,12 +59,11 @@
     void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
             ResultReceiver receiver);
     void sendCustomCommand_impl(Command command, Bundle args);
-    void setPlaylist_impl(List<MediaItem2> playlist);
     void addPlaylistItem_impl(int index, MediaItem2 item);
     void removePlaylistItem_impl(MediaItem2 item);
-    void editPlaylistItem_impl(MediaItem2 item);
     void replacePlaylistItem_impl(int index, MediaItem2 item);
     List<MediaItem2> getPlaylist_impl();
+    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
     MediaItem2 getCurrentPlaylistItem_impl();
     void setPlaylistParams_impl(PlaylistParams params);
     PlaylistParams getPlaylistParams_impl();
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 8697e70..5eb6254 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -20,7 +20,6 @@
 import android.content.Intent;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2.MediaNotification;
-import android.media.PlaybackState2;
 import android.os.IBinder;
 
 /**
diff --git a/media/java/android/media/update/PlaybackState2Provider.java b/media/java/android/media/update/PlaybackState2Provider.java
deleted file mode 100644
index 66b8fa5..0000000
--- a/media/java/android/media/update/PlaybackState2Provider.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.update;
-
-import android.os.Bundle;
-
-/**
- * @hide
- */
-public interface PlaybackState2Provider {
-    String toString_impl();
-
-    int getState_impl();
-
-    long getPosition_impl();
-
-    long getBufferedPosition_impl();
-
-    float getPlaybackSpeed_impl();
-
-    long getLastPositionUpdateTime_impl();
-
-    long getCurrentPlaylistItemIndex_impl();
-
-    Bundle toBundle_impl();
-}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 53ced9c..f78d4a4 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
-import android.media.DataSourceDesc;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaController2;
@@ -32,12 +31,10 @@
 import android.media.MediaMetadata2;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
-import android.media.MediaSession2.CommandButton.Builder;
 import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
-import android.media.PlaybackState2;
 import android.media.Rating2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
@@ -132,10 +129,6 @@
     Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating);
     Rating2 newPercentageRating_Rating2(Context context, float percent);
 
-    PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance, int state,
-            long position, long updateTime, float speed, long bufferedPosition, long activeItemId);
-    PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle);
-
     MediaPlaylistAgentProvider createMediaPlaylistAgent(Context context,
             MediaPlaylistAgent instance);
 }
diff --git a/media/java/android/media/update/TransportControlProvider.java b/media/java/android/media/update/TransportControlProvider.java
index a3fb071..eb03ca7f 100644
--- a/media/java/android/media/update/TransportControlProvider.java
+++ b/media/java/android/media/update/TransportControlProvider.java
@@ -17,7 +17,6 @@
 package android.media.update;
 
 import android.media.MediaItem2;
-import android.media.PlaybackState2;
 
 /**
  * @hide
@@ -34,6 +33,4 @@
     void rewind_impl();
     void seekTo_impl(long pos);
     void skipToPlaylistItem_impl(MediaItem2 item);
-
-    PlaybackState2 getPlaybackState_impl();
 }
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index ff854c5..3a67142 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -25,6 +25,7 @@
 #include <media/IMediaHTTPService.h>
 #include <media/mediametadataretriever.h>
 #include <media/mediascanner.h>
+#include <nativehelper/ScopedLocalRef.h>
 #include <private/media/VideoFrame.h>
 
 #include "jni.h"
@@ -45,6 +46,12 @@
     jmethodID createScaledBitmapMethod;
     jclass configClazz;  // Must be a global ref
     jmethodID createConfigMethod;
+    jclass bitmapParamsClazz; // Must be a global ref
+    jfieldID inPreferredConfig;
+    jfieldID outActualConfig;
+    jclass arrayListClazz; // Must be a global ref
+    jmethodID arrayListInit;
+    jmethodID arrayListAdd;
 };
 
 static fields_t fields;
@@ -254,16 +261,18 @@
 }
 
 static jobject getBitmapFromVideoFrame(
-        JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height) {
+        JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height,
+        SkColorType outColorType) {
     ALOGV("getBitmapFromVideoFrame: dimension = %dx%d and bytes = %d",
             videoFrame->mDisplayWidth,
             videoFrame->mDisplayHeight,
             videoFrame->mSize);
 
-    jobject config = env->CallStaticObjectMethod(
-                        fields.configClazz,
-                        fields.createConfigMethod,
-                        GraphicsJNI::colorTypeToLegacyBitmapConfig(kRGB_565_SkColorType));
+    ScopedLocalRef<jobject> config(env,
+            env->CallStaticObjectMethod(
+                    fields.configClazz,
+                    fields.createConfigMethod,
+                    GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType)));
 
     uint32_t width, height, displayWidth, displayHeight;
     bool swapWidthAndHeight = false;
@@ -285,7 +294,7 @@
                             fields.createBitmapMethod,
                             width,
                             height,
-                            config);
+                            config.get());
     if (jBitmap == NULL) {
         if (env->ExceptionCheck()) {
             env->ExceptionClear();
@@ -297,11 +306,19 @@
     SkBitmap bitmap;
     GraphicsJNI::getSkBitmap(env, jBitmap, &bitmap);
 
-    rotate((uint16_t*)bitmap.getPixels(),
-           (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
-           videoFrame->mWidth,
-           videoFrame->mHeight,
-           videoFrame->mRotationAngle);
+    if (outColorType == kRGB_565_SkColorType) {
+        rotate((uint16_t*)bitmap.getPixels(),
+               (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
+               videoFrame->mWidth,
+               videoFrame->mHeight,
+               videoFrame->mRotationAngle);
+    } else {
+        rotate((uint32_t*)bitmap.getPixels(),
+               (uint32_t*)((char*)videoFrame + sizeof(VideoFrame)),
+               videoFrame->mWidth,
+               videoFrame->mHeight,
+               videoFrame->mRotationAngle);
+    }
 
     if (dst_width <= 0 || dst_height <= 0) {
         dst_width = displayWidth;
@@ -323,12 +340,46 @@
                                 dst_width,
                                 dst_height,
                                 true);
+
+        env->DeleteLocalRef(jBitmap);
         return scaledBitmap;
     }
 
     return jBitmap;
 }
 
+static int getColorFormat(JNIEnv *env, jobject options) {
+    if (options == NULL) {
+        return HAL_PIXEL_FORMAT_RGBA_8888;
+    }
+
+    ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
+    SkColorType prefColorType = GraphicsJNI::getNativeBitmapColorType(env, inConfig.get());
+
+    if (prefColorType == kRGB_565_SkColorType) {
+        return HAL_PIXEL_FORMAT_RGB_565;
+    }
+    return HAL_PIXEL_FORMAT_RGBA_8888;
+}
+
+static SkColorType setOutColorType(JNIEnv *env, int colorFormat, jobject options) {
+    SkColorType outColorType = kN32_SkColorType;
+    if (colorFormat == HAL_PIXEL_FORMAT_RGB_565) {
+        outColorType = kRGB_565_SkColorType;
+    }
+
+    if (options != NULL) {
+        ScopedLocalRef<jobject> config(env,
+                env->CallStaticObjectMethod(
+                        fields.configClazz,
+                        fields.createConfigMethod,
+                        GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType)));
+
+        env->SetObjectField(options, fields.outActualConfig, config.get());
+    }
+    return outColorType;
+}
+
 static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
         JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
 {
@@ -351,11 +402,11 @@
         return NULL;
     }
 
-    return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height);
+    return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, kRGB_565_SkColorType);
 }
 
 static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
-        JNIEnv *env, jobject thiz, jint index)
+        JNIEnv *env, jobject thiz, jint index, jobject params)
 {
     ALOGV("getImageAtIndex: index %d", index);
     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
@@ -364,9 +415,11 @@
         return NULL;
     }
 
+    int colorFormat = getColorFormat(env, params);
+
     // Call native method to retrieve an image
     VideoFrame *videoFrame = NULL;
-    sp<IMemory> frameMemory = retriever->getImageAtIndex(index);
+    sp<IMemory> frameMemory = retriever->getImageAtIndex(index, colorFormat);
     if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
     }
@@ -375,11 +428,13 @@
         return NULL;
     }
 
-    return getBitmapFromVideoFrame(env, videoFrame, -1, -1);
+    SkColorType outColorType = setOutColorType(env, colorFormat, params);
+
+    return getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
 }
 
-static jobjectArray android_media_MediaMetadataRetriever_getFrameAtIndex(
-        JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames)
+static jobject android_media_MediaMetadataRetriever_getFrameAtIndex(
+        JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames, jobject params)
 {
     ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames);
     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
@@ -389,31 +444,34 @@
         return NULL;
     }
 
+    int colorFormat = getColorFormat(env, params);
+
     std::vector<sp<IMemory> > frames;
-    status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames);
+    status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames, colorFormat);
     if (err != OK || frames.size() == 0) {
         ALOGE("failed to get frames from retriever, err=%d, size=%zu",
                 err, frames.size());
         return NULL;
     }
-
-    jobjectArray bitmapArrayObj = env->NewObjectArray(
-            frames.size(), fields.bitmapClazz, NULL);
-    if (bitmapArrayObj == NULL) {
-        ALOGE("can't create bitmap array object");
+    jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit);
+    if (arrayList == NULL) {
+        ALOGE("can't create bitmap array list object");
         return NULL;
     }
 
+    SkColorType outColorType = setOutColorType(env, colorFormat, params);
+
     for (size_t i = 0; i < frames.size(); i++) {
         if (frames[i] == NULL || frames[i]->pointer() == NULL) {
             ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i);
             continue;
         }
         VideoFrame *videoFrame = static_cast<VideoFrame *>(frames[i]->pointer());
-        jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1);
-        env->SetObjectArrayElement(bitmapArrayObj, i, bitmapObj);
+        jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
+        env->CallBooleanMethod(arrayList, fields.arrayListAdd, bitmapObj);
+        env->DeleteLocalRef(bitmapObj);
     }
-    return bitmapArrayObj;
+    return arrayList;
 }
 
 static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
@@ -488,21 +546,21 @@
 // first time an instance of this class is used.
 static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
 {
-    jclass clazz = env->FindClass(kClassPathName);
-    if (clazz == NULL) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass(kClassPathName));
+    if (clazz.get() == NULL) {
         return;
     }
 
-    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
+    fields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
     if (fields.context == NULL) {
         return;
     }
 
-    jclass bitmapClazz = env->FindClass("android/graphics/Bitmap");
-    if (bitmapClazz == NULL) {
+    clazz.reset(env->FindClass("android/graphics/Bitmap"));
+    if (clazz.get() == NULL) {
         return;
     }
-    fields.bitmapClazz = (jclass) env->NewGlobalRef(bitmapClazz);
+    fields.bitmapClazz = (jclass) env->NewGlobalRef(clazz.get());
     if (fields.bitmapClazz == NULL) {
         return;
     }
@@ -521,11 +579,11 @@
         return;
     }
 
-    jclass configClazz = env->FindClass("android/graphics/Bitmap$Config");
-    if (configClazz == NULL) {
+    clazz.reset(env->FindClass("android/graphics/Bitmap$Config"));
+    if (clazz.get() == NULL) {
         return;
     }
-    fields.configClazz = (jclass) env->NewGlobalRef(configClazz);
+    fields.configClazz = (jclass) env->NewGlobalRef(clazz.get());
     if (fields.configClazz == NULL) {
         return;
     }
@@ -535,6 +593,42 @@
     if (fields.createConfigMethod == NULL) {
         return;
     }
+
+    clazz.reset(env->FindClass("android/media/MediaMetadataRetriever$BitmapParams"));
+    if (clazz.get() == NULL) {
+        return;
+    }
+    fields.bitmapParamsClazz = (jclass) env->NewGlobalRef(clazz.get());
+    if (fields.bitmapParamsClazz == NULL) {
+        return;
+    }
+    fields.inPreferredConfig = env->GetFieldID(fields.bitmapParamsClazz,
+            "inPreferredConfig", "Landroid/graphics/Bitmap$Config;");
+    if (fields.inPreferredConfig == NULL) {
+        return;
+    }
+    fields.outActualConfig = env->GetFieldID(fields.bitmapParamsClazz,
+            "outActualConfig", "Landroid/graphics/Bitmap$Config;");
+    if (fields.outActualConfig == NULL) {
+        return;
+    }
+
+    clazz.reset(env->FindClass("java/util/ArrayList"));
+    if (clazz.get() == NULL) {
+        return;
+    }
+    fields.arrayListClazz = (jclass) env->NewGlobalRef(clazz.get());
+    if (fields.arrayListClazz == NULL) {
+        return;
+    }
+    fields.arrayListInit = env->GetMethodID(clazz.get(), "<init>", "()V");
+    if (fields.arrayListInit == NULL) {
+        return;
+    }
+    fields.arrayListAdd = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z");
+    if (fields.arrayListAdd == NULL) {
+        return;
+    }
 }
 
 static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
@@ -556,17 +650,36 @@
             (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
         },
 
-        {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
-        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
-        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
-        {"_getImageAtIndex", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getImageAtIndex},
-        {"_getFrameAtIndex", "(II)[Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtIndex},
-        {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
-        {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
-        {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
-        {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
-        {"native_setup",    "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
-        {"native_init",     "()V", (void *)android_media_MediaMetadataRetriever_native_init},
+        {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V",
+                (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
+        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
+                (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
+        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;",
+                (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
+        {
+            "_getImageAtIndex",
+            "(ILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;",
+            (void *)android_media_MediaMetadataRetriever_getImageAtIndex
+        },
+
+        {
+            "_getFrameAtIndex",
+            "(IILandroid/media/MediaMetadataRetriever$BitmapParams;)Ljava/util/List;",
+            (void *)android_media_MediaMetadataRetriever_getFrameAtIndex
+        },
+
+        {"extractMetadata", "(I)Ljava/lang/String;",
+                (void *)android_media_MediaMetadataRetriever_extractMetadata},
+        {"getEmbeddedPicture", "(I)[B",
+                (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
+        {"release",         "()V",
+                (void *)android_media_MediaMetadataRetriever_release},
+        {"native_finalize", "()V",
+                (void *)android_media_MediaMetadataRetriever_native_finalize},
+        {"native_setup",    "()V",
+                (void *)android_media_MediaMetadataRetriever_native_setup},
+        {"native_init",     "()V",
+                (void *)android_media_MediaMetadataRetriever_native_init},
 };
 
 // This function only registers the native methods, and is called from
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 918b82b..918a375 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1489,7 +1489,7 @@
     {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer2_setVideoSurface},
     {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
-    {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
+    {"_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer2_prepare},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer2_start},
     {"_stop",               "()V",                              (void *)android_media_MediaPlayer2_stop},
@@ -1497,9 +1497,9 @@
     {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer2_getVideoWidth},
     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer2_getVideoHeight},
     {"native_getMetrics",   "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
-    {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer2_setPlaybackParams},
+    {"_setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer2_setPlaybackParams},
     {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer2_getPlaybackParams},
-    {"setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer2_setSyncParams},
+    {"_setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer2_setSyncParams},
     {"getSyncParams",     "()Landroid/media/SyncParams;",   (void *)android_media_MediaPlayer2_getSyncParams},
     {"_seekTo",             "(JI)V",                            (void *)android_media_MediaPlayer2_seekTo},
     {"_notifyAt",           "(J)V",                             (void *)android_media_MediaPlayer2_notifyAt},
@@ -1522,9 +1522,9 @@
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer2_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer2_native_finalize},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer2_get_audio_session_id},
-    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer2_set_audio_session_id},
+    {"_setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer2_set_audio_session_id},
     {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
-    {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer2_attachAuxEffect},
+    {"_attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer2_attachAuxEffect},
     // Modular DRM
     { "_prepareDrm", "([B[B)V",                                 (void *)android_media_MediaPlayer2_prepareDrm },
     { "_releaseDrm", "()V",                                     (void *)android_media_MediaPlayer2_releaseDrm },
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 61e113b..56a242a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,7 +1,6 @@
 package com.android.settingslib;
 
 import android.annotation.ColorInt;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
@@ -142,7 +141,7 @@
     public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
         final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
-            Drawable drawable =  UserIconDrawable.getManagedUserBadgeDrawable(context);
+            Drawable drawable =  UserIconDrawable.getManagedUserDrawable(context);
             drawable.setBounds(0, 0, iconSize, iconSize);
             return drawable;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 7f469b5..54d1aba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.drawable;
 
+import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
@@ -36,6 +37,7 @@
 import android.graphics.Shader;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
 
 import com.android.settingslib.R;
 
@@ -69,15 +71,23 @@
     private float mBadgeMargin;
 
     /**
-     * Gets the system default managed-user badge as a drawable
+     * Gets the system default managed-user badge as a drawable. This drawable is tint-able.
+     * For badging purpose, consider
+     * {@link android.content.pm.PackageManager#getUserBadgedDrawableForDensity(Drawable, UserHandle, Rect, int)}.
+     *
      * @param context
      * @return drawable containing just the badge
      */
-    public static Drawable getManagedUserBadgeDrawable(Context context) {
-        int displayDensity = context.getResources().getDisplayMetrics().densityDpi;
+    public static Drawable getManagedUserDrawable(Context context) {
+        return getDrawableForDisplayDensity
+                (context, com.android.internal.R.drawable.ic_corp_user_badge);
+    }
+
+    private static Drawable getDrawableForDisplayDensity(
+            Context context, @DrawableRes int drawable) {
+        int density = context.getResources().getDisplayMetrics().densityDpi;
         return context.getResources().getDrawableForDensity(
-                com.android.internal.R.drawable.ic_corp_user_badge,
-                displayDensity, context.getTheme());
+                drawable, density, context.getTheme());
     }
 
     /**
@@ -164,7 +174,8 @@
         boolean isManaged = context.getSystemService(DevicePolicyManager.class)
                 .getProfileOwnerAsUser(userId) != null;
         if (isManaged) {
-            badge = getManagedUserBadgeDrawable(context);
+            badge = getDrawableForDisplayDensity(
+                    context, com.android.internal.R.drawable.ic_corp_badge_case);
         }
         return setBadge(badge);
     }
@@ -322,7 +333,6 @@
                     mIntrinsicRadius, mIconPaint);
             canvas.restoreToCount(saveId);
         }
-
         if (mFrameColor != null) {
             mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT));
         }
@@ -343,7 +353,6 @@
             final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin;
             canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius,
                     borderRadius, mClearPaint);
-
             mBadge.draw(canvas);
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 2482095..085e112 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -563,8 +563,7 @@
 
             // If there were no scan results, create an AP for the currently connected network (if
             // it exists).
-            // TODO(sghuman): Investigate if this works for an ephemeral (auto-connected) network
-            // when there are no scan results, as it may not have a valid WifiConfiguration
+            // TODO(b/b/73076869): Add support for passpoint (ephemeral) networks
             if (accessPoints.isEmpty() && connectionConfig != null) {
                 AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
                 activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 54c02a2..e435a72 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -18,9 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -50,6 +52,7 @@
 import android.text.style.TtsSpan;
 
 import com.android.settingslib.R;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.settingslib.wifi.AccessPoint.Speed;
 
 import org.junit.Before;
@@ -61,6 +64,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -79,6 +83,8 @@
     private Context mContext;
     @Mock private RssiCurve mockBadgeCurve;
     @Mock private WifiNetworkScoreCache mockWifiNetworkScoreCache;
+    public static final int NETWORK_ID = 123;
+    public static final int DEFAULT_RSSI = -55;
 
     private static ScanResult createScanResult(String ssid, String bssid, int rssi) {
         ScanResult scanResult = new ScanResult();
@@ -753,16 +759,15 @@
         assertThat(ap.update(config, wifiInfo, newInfo)).isFalse();
     }
     @Test
-    public void testUpdateWithConfigChangeOnly_returnsFalseButInvokesListener() {
-        int networkId = 123;
-        int rssi = -55;
+    public void testUpdateWithConfigChangeOnly_returnsFalseButInvokesListener()
+            throws InterruptedException {
         WifiConfiguration config = new WifiConfiguration();
-        config.networkId = networkId;
+        config.networkId = NETWORK_ID;
         config.numNoInternetAccessReports = 1;
 
         WifiInfo wifiInfo = new WifiInfo();
-        wifiInfo.setNetworkId(networkId);
-        wifiInfo.setRssi(rssi);
+        wifiInfo.setNetworkId(NETWORK_ID);
+        wifiInfo.setRssi(DEFAULT_RSSI);
 
         NetworkInfo networkInfo =
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
@@ -770,8 +775,8 @@
 
         AccessPoint ap = new TestAccessPointBuilder(mContext)
                 .setNetworkInfo(networkInfo)
-                .setNetworkId(networkId)
-                .setRssi(rssi)
+                .setNetworkId(NETWORK_ID)
+                .setRssi(DEFAULT_RSSI)
                 .setWifiInfo(wifiInfo)
                 .build();
 
@@ -781,18 +786,131 @@
         config.validatedInternetAccess = true;
 
         assertThat(ap.update(newConfig, wifiInfo, networkInfo)).isFalse();
+
+        // Wait for MainHandler to process callback
+        CountDownLatch latch = new CountDownLatch(1);
+        ThreadUtils.postOnMainThread(latch::countDown);
+
+        latch.await();
         verify(mockListener).onAccessPointChanged(ap);
     }
 
     @Test
-    public void testUpdateWithNullWifiConfiguration_doesNotThrowNPE() {
-        int networkId = 123;
-        int rssi = -55;
+    public void testConnectionInfo_doesNotThrowNPE_ifListenerIsNulledWhileAwaitingExecution()
+            throws InterruptedException {
         WifiConfiguration config = new WifiConfiguration();
-        config.networkId = networkId;
+        config.networkId = NETWORK_ID;
+        config.numNoInternetAccessReports = 1;
+
         WifiInfo wifiInfo = new WifiInfo();
-        wifiInfo.setNetworkId(networkId);
-        wifiInfo.setRssi(rssi);
+        wifiInfo.setNetworkId(NETWORK_ID);
+        wifiInfo.setRssi(DEFAULT_RSSI);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(NETWORK_ID)
+                .setRssi(DEFAULT_RSSI)
+                .setWifiInfo(wifiInfo)
+                .build();
+
+        WifiConfiguration newConfig = new WifiConfiguration(config);
+        config.validatedInternetAccess = true;
+
+        performGivenUpdateAndThenNullListenerBeforeResumingMainHandlerExecution(
+                ap, () -> assertThat(ap.update(newConfig, wifiInfo, networkInfo)).isFalse());
+
+    }
+
+    private void performGivenUpdateAndThenNullListenerBeforeResumingMainHandlerExecution(
+            AccessPoint ap, Runnable r) {
+        AccessPoint.AccessPointListener mockListener = mock(AccessPoint.AccessPointListener.class);
+        ap.setListener(mockListener);
+
+        // Put a latch on the MainHandler to prevent the callback from being invoked instantly
+        CountDownLatch latch1 = new CountDownLatch(1);
+        ThreadUtils.postOnMainThread(() -> {
+            try{
+                latch1.await();
+            } catch (InterruptedException e) {
+                fail("Interruped Exception thrown while awaiting latch countdown");
+            }
+        });
+
+        r.run();
+
+        ap.setListener(null);
+        latch1.countDown();
+
+        // The second latch ensures the previously posted listener invocation has processed on the
+        // main thread.
+        CountDownLatch latch2 = new CountDownLatch(1);
+        ThreadUtils.postOnMainThread(latch2::countDown);
+
+        try{
+            latch2.await();
+        } catch (InterruptedException e) {
+            fail("Interruped Exception thrown while awaiting latch countdown");
+        }
+    }
+
+    @Test
+    public void testUpdateScanResults_doesNotThrowNPE_ifListenerIsNulledWhileAwaitingExecution()
+            throws InterruptedException {
+        String ssid = "ssid";
+        int newRssi = -80;
+        AccessPoint ap = new TestAccessPointBuilder(mContext).setSsid(ssid).build();
+
+        ScanResult scanResult = new ScanResult();
+        scanResult.SSID = ssid;
+        scanResult.level = newRssi;
+        scanResult.BSSID = "bssid";
+        scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
+        scanResult.capabilities = "";
+
+        performGivenUpdateAndThenNullListenerBeforeResumingMainHandlerExecution(
+                ap, () -> ap.setScanResults(Collections.singletonList(scanResult)));
+    }
+
+    @Test
+    public void testUpdateConfig_doesNotThrowNPE_ifListenerIsNulledWhileAwaitingExecution()
+            throws InterruptedException {
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = NETWORK_ID;
+        config.numNoInternetAccessReports = 1;
+
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(NETWORK_ID);
+        wifiInfo.setRssi(DEFAULT_RSSI);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(NETWORK_ID)
+                .setRssi(DEFAULT_RSSI)
+                .setWifiInfo(wifiInfo)
+                .build();
+
+        WifiConfiguration newConfig = new WifiConfiguration(config);
+        config.validatedInternetAccess = true;
+
+        performGivenUpdateAndThenNullListenerBeforeResumingMainHandlerExecution(
+                ap, () -> ap.update(newConfig));
+    }
+
+    @Test
+    public void testUpdateWithNullWifiConfiguration_doesNotThrowNPE() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = NETWORK_ID;
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(NETWORK_ID);
+        wifiInfo.setRssi(DEFAULT_RSSI);
 
         NetworkInfo networkInfo =
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
@@ -800,8 +918,8 @@
 
         AccessPoint ap = new TestAccessPointBuilder(mContext)
                 .setNetworkInfo(networkInfo)
-                .setNetworkId(networkId)
-                .setRssi(rssi)
+                .setNetworkId(NETWORK_ID)
+                .setRssi(DEFAULT_RSSI)
                 .setWifiInfo(wifiInfo)
                 .build();
 
@@ -847,34 +965,34 @@
                 .thenReturn(buildScoredNetworkWithGivenBadgeCurve(badgeCurve2));
 
         ap.update(
-        mockWifiNetworkScoreCache, true /* scoringUiEnabled */, MAX_SCORE_CACHE_AGE_MILLIS);
+            mockWifiNetworkScoreCache, true /* scoringUiEnabled */, MAX_SCORE_CACHE_AGE_MILLIS);
 
         assertThat(ap.getSpeed()).isEqualTo(speed1);
     }
 
     @Test
     public void testSpeedLabelFallbackScoreIgnoresNullCurves() {
-        int rssi = -55;
         String bssid = "00:00:00:00:00:00";
-        int networkId = 123;
 
         WifiInfo info = new WifiInfo();
-        info.setRssi(rssi);
+        info.setRssi(DEFAULT_RSSI);
         info.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
         info.setBSSID(bssid);
-        info.setNetworkId(networkId);
+        info.setNetworkId(NETWORK_ID);
 
         ArrayList<ScanResult> scanResults = new ArrayList<>();
-        ScanResult scanResultUnconnected = createScanResult(TEST_SSID, "11:11:11:11:11:11", rssi);
+        ScanResult scanResultUnconnected =
+                createScanResult(TEST_SSID, "11:11:11:11:11:11", DEFAULT_RSSI);
         scanResults.add(scanResultUnconnected);
 
-        ScanResult scanResultConnected = createScanResult(TEST_SSID, bssid, rssi);
+        ScanResult scanResultConnected =
+                createScanResult(TEST_SSID, bssid, DEFAULT_RSSI);
         scanResults.add(scanResultConnected);
 
         AccessPoint ap =
                 new TestAccessPointBuilder(mContext)
                         .setActive(true)
-                        .setNetworkId(networkId)
+                        .setNetworkId(NETWORK_ID)
                         .setSsid(TEST_SSID)
                         .setScanResults(scanResults)
                         .setWifiInfo(info)
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index ca965f3..7fb4dc5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -721,7 +721,7 @@
         // mStaleAccessPoints is true
         verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
 
-        assertThat(tracker.getAccessPoints().size()).isEqualTo(1);
+        assertThat(tracker.getAccessPoints()).hasSize(1);
         assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
     }
 
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
index 8d03ce7..622226f 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@*android:color/material_grey_200" />
+    <solid android:color="?android:attr/panelColorBackground" />
     <corners
         android:bottomLeftRadius="@dimen/corner_size"
         android:topLeftRadius="0dp"
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index 478b656..bfabe52 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -39,23 +39,13 @@
             android:theme="@android:style/Theme"
             android:layout_alignParentTop="true"/>
 
-        <!-- This progress bar is the countdown timer. -->
-        <ProgressBar
-            android:id="@+id/countdown_progress"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/car_user_switcher_progress_bar_height"
-            style="@style/CarUserSwitcher.ProgressBar"
-            android:layout_marginTop="@dimen/car_user_switcher_progress_bar_margin_top"
-            android:layout_alignParentTop="true"/>
-
         <com.android.systemui.statusbar.car.UserGridView
             android:id="@+id/user_grid"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginLeft="@dimen/car_margin"
             android:layout_marginRight="@dimen/car_margin"
-            android:layout_marginBottom="@dimen/car_user_grid_margin_bottom"
-            android:layout_centerInParent="true" />
+            android:layout_centerInParent="true"/>
 
         <com.android.systemui.statusbar.car.PageIndicator
             android:id="@+id/user_switcher_page_indicator"
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index 7844cac..447970c 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -42,7 +42,6 @@
             android:layout_height="wrap_content"
             android:layout_marginLeft="@dimen/car_margin"
             android:layout_marginRight="@dimen/car_margin"
-            android:layout_marginBottom="@dimen/car_user_grid_margin_bottom"
             android:layout_above="@id/user_switcher_page_indicator" />
 
         <com.android.systemui.statusbar.car.PageIndicator
diff --git a/packages/SystemUI/res/layout/fingerprint_dialog.xml b/packages/SystemUI/res/layout/fingerprint_dialog.xml
index 1b47489..1bdaf6e 100644
--- a/packages/SystemUI/res/layout/fingerprint_dialog.xml
+++ b/packages/SystemUI/res/layout/fingerprint_dialog.xml
@@ -26,116 +26,138 @@
     <View
         android:id="@+id/space"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="0dp"
         android:layout_weight="1" />
 
     <LinearLayout
-        android:id="@+id/dialog"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:elevation="2dp"
-        android:background="@drawable/fingerprint_dialog_bg">
+        android:layout_height="wrap_content">
 
-        <TextView
-            android:id="@+id/title"
-            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="24dp"
-            android:layout_marginStart="24dp"
-            android:layout_marginTop="24dp"
-            android:gravity="@integer/fingerprint_dialog_text_gravity"
-            android:textSize="20sp"
-            android:maxLines="1"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:marqueeRepeatLimit="marquee_forever"
-            android:textColor="@color/fingerprint_dialog_text_dark_color"/>
-
-        <TextView
-            android:id="@+id/subtitle"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="8dp"
-            android:layout_marginStart="24dp"
-            android:layout_marginEnd="24dp"
-            android:gravity="@integer/fingerprint_dialog_text_gravity"
-            android:textSize="16sp"
-            android:maxLines="1"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:marqueeRepeatLimit="marquee_forever"
-            android:textColor="@color/fingerprint_dialog_text_dark_color"/>
-
-        <TextView
-            android:id="@+id/description"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="24dp"
-            android:layout_marginStart="24dp"
-            android:gravity="@integer/fingerprint_dialog_text_gravity"
-            android:paddingTop="8dp"
-            android:textSize="16sp"
-            android:maxLines="4"
-            android:textColor="@color/fingerprint_dialog_text_dark_color"/>
-
-        <ImageView
-            android:id="@+id/fingerprint_icon"
-            android:layout_width="@dimen/fingerprint_dialog_fp_icon_size"
-            android:layout_height="@dimen/fingerprint_dialog_fp_icon_size"
-            android:layout_gravity="center_horizontal"
-            android:layout_marginTop="48dp"
-            android:scaleType="fitXY"
-            android:contentDescription="@string/accessibility_fingerprint_dialog_fingerprint_icon" />
-
-        <TextView
-            android:id="@+id/error"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="24dp"
-            android:layout_marginStart="24dp"
-            android:paddingTop="16dp"
-            android:paddingBottom="24dp"
-            android:textSize="12sp"
-            android:gravity="center_horizontal"
-            android:accessibilityLiveRegion="polite"
-            android:text="@string/fingerprint_dialog_touch_sensor"
-            android:contentDescription="@string/accessibility_fingerprint_dialog_help_area"
-            android:textColor="@color/fingerprint_dialog_text_light_color"/>
+        <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
+         on horizontal/portrait orientation -->
+        <View
+            android:id="@+id/left_space"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"/>
 
         <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="72dip"
-            android:paddingTop="24dp"
-            android:layout_gravity="center_vertical"
-            style="?android:attr/buttonBarStyle"
-            android:orientation="horizontal"
-            android:measureWithLargestChild="true">
-            <Space android:id="@+id/leftSpacer"
-                android:layout_width="24dp"
-                android:layout_height="match_parent"
-                android:visibility="visible" />
-            <!-- Negative Button -->
-            <Button android:id="@+id/button2"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                android:layout_marginStart="-12dp"
-                android:gravity="start|center_vertical"
-                android:maxLines="2" />
-            <!-- Positive Button -->
-            <Button android:id="@+id/button1"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                android:layout_marginEnd="12dp"
-                android:maxLines="2" />
-            <Space android:id="@+id/rightSpacer"
-                android:layout_width="24dip"
-                android:layout_height="match_parent"
-                android:visibility="gone" />
+            android:id="@+id/dialog"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:elevation="2dp"
+            android:background="@drawable/fingerprint_dialog_bg">
+
+            <TextView
+                android:id="@+id/title"
+                android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="24dp"
+                android:layout_marginStart="24dp"
+                android:layout_marginTop="24dp"
+                android:gravity="@integer/fingerprint_dialog_text_gravity"
+                android:textSize="20sp"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:ellipsize="marquee"
+                android:marqueeRepeatLimit="marquee_forever"
+                android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+
+            <TextView
+                android:id="@+id/subtitle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:layout_marginStart="24dp"
+                android:layout_marginEnd="24dp"
+                android:gravity="@integer/fingerprint_dialog_text_gravity"
+                android:textSize="16sp"
+                android:maxLines="1"
+                android:singleLine="true"
+                android:ellipsize="marquee"
+                android:marqueeRepeatLimit="marquee_forever"
+                android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+
+            <TextView
+                android:id="@+id/description"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="24dp"
+                android:layout_marginStart="24dp"
+                android:gravity="@integer/fingerprint_dialog_text_gravity"
+                android:paddingTop="8dp"
+                android:textSize="16sp"
+                android:maxLines="4"
+                android:textColor="@color/fingerprint_dialog_text_dark_color"/>
+
+            <ImageView
+                android:id="@+id/fingerprint_icon"
+                android:layout_width="@dimen/fingerprint_dialog_fp_icon_size"
+                android:layout_height="@dimen/fingerprint_dialog_fp_icon_size"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="48dp"
+                android:scaleType="fitXY"
+                android:contentDescription="@string/accessibility_fingerprint_dialog_fingerprint_icon" />
+
+            <TextView
+                android:id="@+id/error"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="24dp"
+                android:layout_marginStart="24dp"
+                android:paddingTop="16dp"
+                android:paddingBottom="24dp"
+                android:textSize="12sp"
+                android:gravity="center_horizontal"
+                android:accessibilityLiveRegion="polite"
+                android:text="@string/fingerprint_dialog_touch_sensor"
+                android:contentDescription="@string/accessibility_fingerprint_dialog_help_area"
+                android:textColor="@color/fingerprint_dialog_text_light_color"/>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="72dip"
+                android:paddingTop="24dp"
+                android:layout_gravity="center_vertical"
+                style="?android:attr/buttonBarStyle"
+                android:orientation="horizontal"
+                android:measureWithLargestChild="true">
+                <Space android:id="@+id/leftSpacer"
+                    android:layout_width="24dp"
+                    android:layout_height="match_parent"
+                    android:visibility="visible" />
+                <!-- Negative Button -->
+                <Button android:id="@+id/button2"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                    android:layout_marginStart="-12dp"
+                    android:gravity="start|center_vertical"
+                    android:maxLines="2" />
+                <!-- Positive Button -->
+                <Button android:id="@+id/button1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                    android:layout_marginEnd="12dp"
+                    android:maxLines="2" />
+                <Space android:id="@+id/rightSpacer"
+                    android:layout_width="24dip"
+                    android:layout_height="match_parent"
+                    android:visibility="gone" />
+            </LinearLayout>
         </LinearLayout>
+
+        <!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
+         on horizontal/portrait orientation -->
+        <View
+            android:id="@+id/right_space"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:layout_height="match_parent" />
+
     </LinearLayout>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 0063f6a..0a3f4eb 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -73,17 +73,19 @@
                     <!-- volume rows added and removed here! :-) -->
             </LinearLayout>
             <FrameLayout
-                android:layout_height="wrap_content"
+                android:id="@+id/settings_container"
                 android:layout_width="match_parent"
+                android:layout_height="wrap_content"
                 android:background="@drawable/rounded_bg_bottom_background">
                 <com.android.keyguard.AlphaOptimizedImageButton
                     android:id="@+id/settings"
-                    android:src="@drawable/ic_settings"
+                    android:src="@drawable/ic_settings_16dp"
                     android:layout_width="@dimen/volume_dialog_tap_target_size"
                     android:layout_height="@dimen/volume_dialog_tap_target_size"
                     android:layout_gravity="center"
+                    android:contentDescription="@string/accessibility_volume_settings"
                     android:background="?android:selectableItemBackgroundBorderless"
-                    android:tint="#8A000000"
+                    android:tint="?android:attr/colorControlNormal"
                     android:soundEffectsEnabled="false" />
             </FrameLayout>
         </LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index def6f6b..bcc3692 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -25,7 +25,6 @@
     <LinearLayout
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
-        android:layout_marginTop="@dimen/volume_dialog_slider_margin_top"
         android:gravity="center"
         android:layout_gravity="center"
         android:orientation="vertical" >
@@ -42,6 +41,8 @@
         <FrameLayout
             android:id="@+id/volume_row_slider_frame"
             android:layout_width="match_parent"
+            android:layout_marginTop="@dimen/volume_dialog_slider_margin_top"
+            android:layout_marginBottom="@dimen/volume_dialog_slider_margin_bottom"
             android:layoutDirection="rtl"
             android:layout_height="@dimen/volume_dialog_slider_height">
             <SeekBar
@@ -60,6 +61,7 @@
             android:layout_width="@dimen/volume_dialog_tap_target_size"
             android:layout_height="@dimen/volume_dialog_tap_target_size"
             android:background="?android:selectableItemBackgroundBorderless"
+            android:layout_marginBottom="@dimen/volume_dialog_row_margin_bottom"
             android:tint="@color/accent_tint_color_selector"
             android:soundEffectsEnabled="false" />
     </LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 52b053b..e30b86a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -270,7 +270,7 @@
 
     <dimen name="volume_dialog_panel_width">64dp</dimen>
 
-    <dimen name="volume_dialog_slider_height">101dp</dimen>
+    <dimen name="volume_dialog_slider_height">108dp</dimen>
 
     <dimen name="volume_dialog_row_height">252dp</dimen>
 
@@ -280,7 +280,13 @@
 
     <dimen name="volume_dialog_spacer">4dp</dimen>
 
-    <dimen name="volume_dialog_slider_margin_top">13dp</dimen>
+    <dimen name="volume_dialog_slider_margin_top">22dp</dimen>
+
+    <dimen name="volume_dialog_slider_margin_bottom">-2dp</dimen>
+
+    <dimen name="volume_dialog_row_margin_bottom">8dp</dimen>
+
+    <dimen name="volume_dialog_settings_icon_size">16dp</dimen>
 
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index f3c9f89..5679dd2 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -36,8 +36,6 @@
     <dimen name="car_page_indicator_dot_diameter">12dp</dimen>
     <dimen name="car_page_indicator_margin_bottom">24dp</dimen>
 
-    <dimen name="car_user_switcher_progress_bar_height">6dp</dimen>
-    <dimen name="car_user_switcher_progress_bar_margin_top">@dimen/status_bar_height</dimen>
     <dimen name="car_start_driving_corner_radius">16dp</dimen>
     <dimen name="car_start_driving_padding_side">30dp</dimen>
     <dimen name="car_start_driving_height">80dp</dimen>
@@ -57,7 +55,6 @@
     <dimen name="car_user_switcher_container_height">420dp</dimen>
     <!-- This must be the negative of car_user_switcher_container_height for the animation. -->
     <dimen name="car_user_switcher_container_anim_height">-420dp</dimen>
-    <dimen name="car_user_grid_margin_bottom">28dp</dimen>
 
     <dimen name="car_body2_size">26sp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/SystemUI/res/values/integers_car.xml
index f84dd4b..a462576 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/SystemUI/res/values/integers_car.xml
@@ -16,8 +16,5 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <integer name="car_user_switcher_timeout_ms">15000</integer>
-    <!-- This values less than ProgressBar.PROGRESS_ANIM_DURATION for a smooth animation. -->
-    <integer name="car_user_switcher_anim_update_ms">60</integer>
     <integer name="car_user_switcher_anim_cascade_delay_ms">27</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b7c1c55..814b3ef 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -377,6 +377,9 @@
     <!-- Content description of the data connection type 3.5G. [CHAR LIMIT=NONE] -->
     <string name="data_connection_3_5g">3.5G</string>
 
+    <!-- Content description of the data connection type 3.5G+. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_3_5g_plus">3.5G+</string>
+
     <!-- Content description of the data connection type 4G . [CHAR LIMIT=NONE] -->
     <string name="data_connection_4g">4G</string>
 
@@ -1271,6 +1274,9 @@
     <!-- Button label for ending zen mode in the volume dialog -->
     <string name="volume_zen_end_now">Turn off now</string>
 
+    <!-- Content description for accessibility (not shown on the screen): volume dialog settings button. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_volume_settings">Sound settings</string>
+
     <!-- Content description for accessibility (not shown on the screen): volume dialog expand button. [CHAR LIMIT=NONE] -->
     <string name="accessibility_volume_expand">Expand</string>
 
diff --git a/packages/SystemUI/res/values/styles_car.xml b/packages/SystemUI/res/values/styles_car.xml
index c66792c..2aaef86 100644
--- a/packages/SystemUI/res/values/styles_car.xml
+++ b/packages/SystemUI/res/values/styles_car.xml
@@ -16,14 +16,6 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="CarUserSwitcher.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
-        <item name="android:progressDrawable">@drawable/car_progress_bar</item>
-        <item name="android:min">0</item>
-        <item name="android:max">@integer/car_user_switcher_timeout_ms</item>
-        <item name="android:progress">0</item>
-        <item name="android:interpolator">@android:anim/linear_interpolator</item>
-    </style>
-
     <style name="CarUserSwitcher.StartDrivingButton" parent="@android:style/Widget.Material.Button">
         <item name="android:background">@drawable/car_round_button</item>
         <item name="android:textSize">@dimen/car_start_driving_text_size</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
index 3bc1d9a..14767f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
@@ -103,12 +103,13 @@
         int userId = taskKey.userId;
         Bitmap tdIcon = desc.getInMemoryIcon();
         if (tdIcon != null) {
-            return createDrawableFromBitmap(tdIcon, userId);
+            return createDrawableFromBitmap(tdIcon, userId, desc);
         }
         if (desc.getIconResource() != 0) {
             // TODO: Use task context here
             try {
-                return createBadgedDrawable(mContext.getDrawable(desc.getIconResource()), userId);
+                return createBadgedDrawable(
+                        mContext.getDrawable(desc.getIconResource()), userId, desc);
             } catch (Resources.NotFoundException e) {
                 Log.e(TAG, "Could not find icon drawable from resource", e);
             }
@@ -117,13 +118,13 @@
         tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
                 desc.getIconFilename(), userId);
         if (tdIcon != null) {
-            return createDrawableFromBitmap(tdIcon, userId);
+            return createDrawableFromBitmap(tdIcon, userId, desc);
         }
 
         // Load the icon from the activity info and cache it
         ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
         if (activityInfo != null) {
-            Drawable icon = getBadgedActivityIcon(activityInfo, userId);
+            Drawable icon = getBadgedActivityIcon(activityInfo, userId, desc);
             if (icon != null) {
                 return icon;
             }
@@ -135,16 +136,20 @@
 
     public abstract Drawable getDefaultIcon(int userId);
 
-    protected Drawable createDrawableFromBitmap(Bitmap icon, int userId) {
-        return createBadgedDrawable(new BitmapDrawable(mContext.getResources(), icon), userId);
+    protected Drawable createDrawableFromBitmap(Bitmap icon, int userId,
+            ActivityManager.TaskDescription desc) {
+        return createBadgedDrawable(
+                new BitmapDrawable(mContext.getResources(), icon), userId, desc);
     }
 
-    protected abstract Drawable createBadgedDrawable(Drawable icon, int userId);
+    protected abstract Drawable createBadgedDrawable(Drawable icon, int userId,
+            ActivityManager.TaskDescription desc);
 
     /**
      * @return the activity icon for the ActivityInfo for a user, badging if necessary.
      */
-    protected abstract Drawable getBadgedActivityIcon(ActivityInfo info, int userId);
+    protected abstract Drawable getBadgedActivityIcon(ActivityInfo info, int userId,
+            ActivityManager.TaskDescription desc);
 
     public static class DefaultIconLoader extends IconLoader {
 
@@ -168,7 +173,8 @@
         }
 
         @Override
-        protected Drawable createBadgedDrawable(Drawable icon, int userId) {
+        protected Drawable createBadgedDrawable(Drawable icon, int userId,
+                ActivityManager.TaskDescription desc) {
             if (userId != UserHandle.myUserId()) {
                 icon = mContext.getPackageManager().getUserBadgedIcon(icon, new UserHandle(userId));
             }
@@ -176,7 +182,8 @@
         }
 
         @Override
-        protected Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
+        protected Drawable getBadgedActivityIcon(ActivityInfo info, int userId,
+                ActivityManager.TaskDescription desc) {
             return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index cad155c..5fce0a6 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -45,6 +45,7 @@
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.LightBarController;
@@ -317,6 +318,8 @@
 
         mProviders.put(AppOpsListener.class, () -> new AppOpsListener(mContext));
 
+        mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
+
         // Put all dependencies above here so the factory can override them if it wants.
         SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
index 37e1936..ebdc703 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.fingerprint;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -27,6 +28,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -42,6 +44,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
@@ -74,6 +77,8 @@
     private final LinearLayout mDialog;
     private int mLastState;
 
+    private final float mDisplayWidth;
+
     public FingerprintDialogView(Context context, Handler handler) {
         super(context);
         mHandler = handler;
@@ -88,6 +93,10 @@
         mFingerprintColor = Color.parseColor(
                 getResources().getString(R.color.fingerprint_dialog_fingerprint_color));
 
+        DisplayMetrics metrics = new DisplayMetrics();
+        mWindowManager.getDefaultDisplay().getMetrics(metrics);
+        mDisplayWidth = metrics.widthPixels;
+
         // Create the dialog
         LayoutInflater factory = LayoutInflater.from(getContext());
         mLayout = (ViewGroup) factory.inflate(R.layout.fingerprint_dialog, this, false);
@@ -117,15 +126,14 @@
         });
 
         final View space = mLayout.findViewById(R.id.space);
+        final View leftSpace = mLayout.findViewById(R.id.left_space);
+        final View rightSpace = mLayout.findViewById(R.id.right_space);
         final Button negative = mLayout.findViewById(R.id.button2);
         final Button positive = mLayout.findViewById(R.id.button1);
 
-        space.setClickable(true);
-        space.setOnTouchListener((View view, MotionEvent event) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, true /* userCanceled */)
-                    .sendToTarget();
-            return true;
-        });
+        setDismissesDialog(space);
+        setDismissesDialog(leftSpace);
+        setDismissesDialog(rightSpace);
 
         negative.setOnClickListener((View v) -> {
             mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
@@ -149,6 +157,8 @@
         final Button negative = mLayout.findViewById(R.id.button2);
         final Button positive = mLayout.findViewById(R.id.button1);
 
+        mDialog.getLayoutParams().width = (int) mDisplayWidth;
+
         mLastState = STATE_NONE;
         updateFingerprintIcon(STATE_FINGERPRINT);
 
@@ -189,6 +199,15 @@
         });
     }
 
+    private void setDismissesDialog(View v) {
+        v.setClickable(true);
+        v.setOnTouchListener((View view, MotionEvent event) -> {
+            mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, true /* userCanceled */)
+                    .sendToTarget();
+            return true;
+        });
+    }
+
     public void startDismiss() {
         final Runnable endActionRunnable = new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 3ec8913..bc353f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -19,7 +19,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.res.Resources;
-import android.os.CountDownTimer;
 import android.view.View;
 import android.view.ViewStub;
 import android.widget.ProgressBar;
@@ -36,16 +35,11 @@
     private final View mParent;
     private final UserGridView mUserGridView;
     private final UserSwitcherController mUserSwitcherController;
-    private final ProgressBar mProgressBar;
     private final ProgressBar mSwitchingUsers;
-    private final int mLoginTimeoutMs;
-    private final int mAnimUpdateIntervalMs;
     private final int mShortAnimDuration;
 
     private boolean mShowing;
 
-    private CountDownTimer mTimer;
-
     public FullscreenUserSwitcher(StatusBar statusBar,
             UserSwitcherController userSwitcherController,
             ViewStub containerStub) {
@@ -63,26 +57,13 @@
         PageIndicator pageIndicator = mContainer.findViewById(R.id.user_switcher_page_indicator);
         pageIndicator.setupWithViewPager(mUserGridView);
 
-        mProgressBar = mContainer.findViewById(R.id.countdown_progress);
         Resources res = mContainer.getResources();
-        mLoginTimeoutMs = res.getInteger(R.integer.car_user_switcher_timeout_ms);
-        mAnimUpdateIntervalMs = res.getInteger(R.integer.car_user_switcher_anim_update_ms);
         mShortAnimDuration = res.getInteger(android.R.integer.config_shortAnimTime);
 
         mContainer.findViewById(R.id.start_driving).setOnClickListener(v -> {
-            cancelTimer();
             automaticallySelectUser();
         });
 
-        // Any interaction with the screen should cancel the timer.
-        mContainer.setOnClickListener(v -> {
-            cancelTimer();
-        });
-        mUserGridView.setOnTouchListener((v, e) -> {
-            cancelTimer();
-            return false;
-        });
-
         mSwitchingUsers = mParent.findViewById(R.id.switching_users);
     }
 
@@ -127,44 +108,14 @@
         }
         mShowing = true;
         mParent.setVisibility(View.VISIBLE);
-        cancelTimer();
-
-        // This would be the case if we were in the middle of a switch.
-        if (mProgressBar.getVisibility() != View.VISIBLE) {
-            return;
-        }
-
-        mTimer = new CountDownTimer(mLoginTimeoutMs, mAnimUpdateIntervalMs) {
-            @Override
-            public void onTick(long msUntilFinished) {
-                int elapsed = mLoginTimeoutMs - (int) msUntilFinished;
-                mProgressBar.setProgress((int) elapsed, true /* animate */);
-            }
-
-            @Override
-            public void onFinish() {
-                mProgressBar.setProgress(mLoginTimeoutMs, true /* animate */);
-                automaticallySelectUser();
-            }
-        };
-        mTimer.start();
     }
 
     public void hide() {
         mShowing = false;
-        cancelTimer();
         toggleSwitchInProgress(false);
         mParent.setVisibility(View.GONE);
     }
 
-    private void cancelTimer() {
-        if (mTimer != null) {
-            mTimer.cancel();
-            mTimer = null;
-            mProgressBar.setProgress(0, true /* animate */);
-        }
-    }
-
     private void automaticallySelectUser() {
         // TODO: Switch according to some policy. This implementation just tries to drop the
         //       keyguard for the current user.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index ca66e98..0716b37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -55,6 +55,7 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
@@ -912,4 +913,16 @@
             return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
         }
     }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        int bottom = insets.getDisplayCutout() != null
+                ? insets.getDisplayCutout().getSafeInsetBottom() : 0;
+        if (isPaddingRelative()) {
+            setPaddingRelative(getPaddingStart(), getPaddingTop(), getPaddingEnd(), bottom);
+        } else {
+            setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottom);
+        }
+        return insets;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ca5a350..e97fa85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -50,6 +50,7 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
@@ -246,7 +247,7 @@
 
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
         mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
-        mVibratorHelper = new VibratorHelper(context);
+        mVibratorHelper = Dependency.get(VibratorHelper.class);
 
         mConfiguration = new Configuration();
         mConfiguration.updateFrom(context.getResources().getConfiguration());
@@ -1035,6 +1036,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
+        requestApplyInsets();
         reorient();
         onPluginDisconnected(null); // Create default gesture helper
         Dependency.get(PluginManager.class).addPluginListener(this,
@@ -1112,6 +1114,13 @@
         pw.println("    }");
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        setPadding(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+        return super.onApplyWindowInsets(insets);
+    }
+
     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
         pw.print("      " + caption + ": ");
         if (button == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 3de0a41..7f1e9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -38,6 +38,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
 import com.android.systemui.DejankUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
@@ -207,7 +208,7 @@
         mFalsingManager = FalsingManager.getInstance(context);
         mNotificationsDragEnabled =
                 getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
-        mVibratorHelper = new VibratorHelper(context);
+        mVibratorHelper = Dependency.get(VibratorHelper.class);
         mVibrateOnOpening = mContext.getResources().getBoolean(
                 R.bool.config_vibrateOnIconAnimation);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 71376a5..f8b4e07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -126,6 +126,7 @@
     private float mExpansionFraction = 1f;
 
     private boolean mDarkenWhileDragging;
+    private boolean mExpansionAffectsAlpha = true;
     protected boolean mAnimateChange;
     private boolean mUpdatePending;
     private boolean mTracking;
@@ -381,6 +382,10 @@
     }
 
     private void applyExpansionToAlpha() {
+        if (!mExpansionAffectsAlpha) {
+            return;
+        }
+
         if (mState == ScrimState.UNLOCKED) {
             // Darken scrim as you pull down the shade when unlocked
             float behindFraction = getInterpolatedFraction();
@@ -912,6 +917,10 @@
         mScreenOn = false;
     }
 
+    public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
+        mExpansionAffectsAlpha = expansionAffectsAlpha;
+    }
+
     public interface Callback {
         default void onStart() {
         }
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 a305bfc..e4f142a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3688,6 +3688,7 @@
     public void finishKeyguardFadingAway() {
         mKeyguardFadingAway = false;
         mKeyguardMonitor.notifyKeyguardDoneFading();
+        mScrimController.setExpansionAffectsAlpha(true);
     }
 
     // TODO: Move this to NotificationLockscreenUserManager.
@@ -4618,9 +4619,14 @@
         final boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
                 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
 
+        // Do not animate the scrim expansion when it's triggered by the fingerprint sensor.
+        mScrimController.setExpansionAffectsAlpha(mFingerprintUnlockController.getMode()
+                != FingerprintUnlockController.MODE_UNLOCK);
+
         if (mBouncerShowing) {
-            mScrimController.transitionTo(
-                    mIsOccluded ? ScrimState.BOUNCER_OCCLUDED : ScrimState.BOUNCER);
+            final boolean qsExpanded = mQSPanel != null && mQSPanel.isExpanded();
+            mScrimController.transitionTo(mIsOccluded || qsExpanded ?
+                    ScrimState.BOUNCER_OCCLUDED : ScrimState.BOUNCER);
         } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         } else if (mBrightnessMirrorVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 81641da..3bcfd4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -126,7 +126,7 @@
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         mRipple = new KeyButtonRipple(context, this);
-        mVibratorHelper = new VibratorHelper(context);
+        mVibratorHelper = Dependency.get(VibratorHelper.class);
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
         setBackground(mRipple);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 487d1c5..a046675 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -205,13 +205,15 @@
         }
 
         MobileIconGroup hGroup = TelephonyIcons.THREE_G;
+        MobileIconGroup hPlusGroup = TelephonyIcons.THREE_G;
         if (mConfig.hspaDataDistinguishable) {
             hGroup = TelephonyIcons.H;
+            hPlusGroup = TelephonyIcons.H_PLUS;
         }
         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup);
+        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hPlusGroup);
 
         if (mConfig.show4gForLte) {
             mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 5363742..2258fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -884,6 +884,7 @@
                             datatype.equals("e") ? TelephonyIcons.E :
                             datatype.equals("g") ? TelephonyIcons.G :
                             datatype.equals("h") ? TelephonyIcons.H :
+                            datatype.equals("h+") ? TelephonyIcons.H_PLUS :
                             datatype.equals("lte") ? TelephonyIcons.LTE :
                             datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
                             datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED :
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 986abef..7e6fe02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -28,7 +28,6 @@
     static final int ICON_G = R.drawable.ic_g_mobiledata;
     static final int ICON_E = R.drawable.ic_e_mobiledata;
     static final int ICON_H = R.drawable.ic_h_mobiledata;
-    // TODO: add logic to insert H+ icon
     static final int ICON_H_PLUS = R.drawable.ic_h_plus_mobiledata;
     static final int ICON_3G = R.drawable.ic_3g_mobiledata;
     static final int ICON_4G = R.drawable.ic_4g_mobiledata;
@@ -135,6 +134,19 @@
             TelephonyIcons.ICON_H,
             false);
 
+    static final MobileIconGroup H_PLUS = new MobileIconGroup(
+            "H+",
+            null,
+            null,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+            0, 0,
+            0,
+            0,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+            R.string.data_connection_3_5g_plus,
+            TelephonyIcons.ICON_H_PLUS,
+            false);
+
     static final MobileIconGroup FOUR_G = new MobileIconGroup(
             "4G",
             null,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index fe21f87..22bf983 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -23,6 +23,8 @@
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
 
 import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
 
@@ -81,6 +83,7 @@
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.plugins.VolumeDialogController.StreamState;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -102,6 +105,7 @@
     private final Context mContext;
     private final H mHandler = new H();
     private final VolumeDialogController mController;
+    private final DeviceProvisionedController mDeviceProvisionedController;
 
     private Window mWindow;
     private CustomDialog mDialog;
@@ -109,6 +113,7 @@
     private ViewGroup mDialogRowsView;
     private ViewGroup mRinger;
     private ImageButton mRingerIcon;
+    private View mSettingsView;
     private ImageButton mSettingsIcon;
     private ImageView mZenIcon;
     private final List<VolumeRow> mRows = new ArrayList<>();
@@ -139,6 +144,7 @@
         mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
         mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
         mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
+        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
     }
 
     public void init(int windowType, Callback callback) {
@@ -212,6 +218,7 @@
         mRinger = mDialog.findViewById(R.id.ringer);
         mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
         mZenIcon = mRinger.findViewById(R.id.dnd_icon);
+        mSettingsView = mDialog.findViewById(R.id.settings_container);
         mSettingsIcon = mDialog.findViewById(R.id.settings);
 
         if (mRows.isEmpty()) {
@@ -387,6 +394,8 @@
     }
 
     public void initSettingsH() {
+        mSettingsView.setVisibility(
+                mDeviceProvisionedController.isDeviceProvisioned() ? VISIBLE : GONE);
         mSettingsIcon.setOnClickListener(v -> {
             Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -607,7 +616,7 @@
      * @param enable whether to enable volume row views and hide dnd icon
      */
     private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
-        row.dndIcon.setVisibility(enable ? View.GONE : View.VISIBLE);
+        row.dndIcon.setVisibility(enable ? GONE : VISIBLE);
     }
 
     /**
@@ -617,7 +626,7 @@
      */
     private void enableRingerViewsH(boolean enable) {
         mRingerIcon.setEnabled(enable);
-        mZenIcon.setVisibility(enable ? View.GONE : View.VISIBLE);
+        mZenIcon.setVisibility(enable ? GONE : VISIBLE);
     }
 
     private void trimObsoleteH() {
@@ -797,7 +806,7 @@
         }
         final int progress = row.slider.getProgress();
         final int level = getImpliedLevel(row.slider, progress);
-        final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
+        final boolean rowVisible = row.view.getVisibility() == VISIBLE;
         final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
                 < USER_ATTEMPT_GRACE_PERIOD;
         mHandler.removeMessages(H.RECHECK, row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index bfa469e..6fbc0d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -206,6 +206,25 @@
     }
 
     @Test
+    public void panelExpansionAffectsAlpha() {
+        mScrimController.setPanelExpansion(0f);
+        mScrimController.setPanelExpansion(0.5f);
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.finishAnimationsImmediately();
+
+        final float scrimAlpha = mScrimBehind.getViewAlpha();
+        mScrimController.setExpansionAffectsAlpha(false);
+        mScrimController.setPanelExpansion(0.8f);
+        Assert.assertEquals("Scrim opacity shouldn't change when setExpansionAffectsAlpha "
+                + "is false", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+
+        mScrimController.setExpansionAffectsAlpha(true);
+        mScrimController.setPanelExpansion(0.1f);
+        Assert.assertNotEquals("Scrim opacity should change when setExpansionAffectsAlpha "
+                + "is true", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+    }
+
+    @Test
     public void transitionToUnlockedFromAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 8629799..365a9b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -74,6 +74,17 @@
         verifyDataIndicators(TelephonyIcons.ICON_H);
     }
 
+
+    @Test
+    public void testHspaPlusDataIcon() {
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_HSPAP);
+
+        verifyDataIndicators(TelephonyIcons.ICON_H_PLUS);
+    }
+
+
     @Test
     public void testWfcNoDataIcon() {
         setupDefaultSignal();
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
new file mode 100644
index 0000000..d83b30a
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationDoubleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..5d3385d
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.internal.display.cutout.emulation.double"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="com.android.internal.display_cutout_emulation"
+        android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
new file mode 100644
index 0000000..ca261f9
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
@@ -0,0 +1,67 @@
+<!--
+  ~ 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">
+
+    <!-- The bounding path of the cutout region of the main built-in display.
+         Must either be empty if there is no cutout region, or a string that is parsable by
+         {@link android.util.PathParser}.
+
+         The path is assumed to be specified in display coordinates with pixel units and in
+         the display's native orientation, with the origin of the coordinate system at the
+         center top of the display.
+
+         To facilitate writing device-independent emulation overlays, the marker `@dp` can be
+         appended after the path string to interpret coordinates in dp instead of px units.
+         Note that a physical cutout should be configured in pixels for the best results.
+         -->
+    <string translatable="false" name="config_mainBuiltInDisplayCutout">
+        M 0,0
+        L -72, 0
+        L -69.9940446283, 20.0595537175
+        C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0
+        L 56.8, 32.0
+        C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175
+        L 72, 0
+        Z
+        @bottom
+        M 0,0
+        L -72, 0
+        L -69.9940446283, -20.0595537175
+        C -69.1582133885, -28.4178661152 -65.2, -32.0 -56.8, -32.0
+        L 56.8, -32.0
+        C 65.2, -32.0 69.1582133885, -28.4178661152 69.9940446283, -20.0595537175
+        L 72, 0
+        Z
+        @dp
+    </string>
+
+    <!-- Whether the display cutout region of the main built-in display should be forced to
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+     -->
+    <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
+
+    <!-- Height of the status bar -->
+    <dimen name="status_bar_height_portrait">48dp</dimen>
+    <dimen name="status_bar_height_landscape">28dp</dimen>
+    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
+    <dimen name="quick_qs_offset_height">48dp</dimen>
+    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
+    <dimen name="quick_qs_total_height">176dp</dimen>
+
+</resources>
+
+
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.xml
new file mode 100644
index 0000000..68c2dcb
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/strings.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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="display_cutout_emulation_overlay">Double display cutout</string>
+
+</resources>
+
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
index ddac106..41a2940 100644
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
+++ b/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
@@ -7,5 +7,6 @@
         <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
         <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
         <item name="android:colorBackgroundFloating">@*android:color/material_grey_900</item>
+        <item name="android:panelColorBackground">@*android:color/material_grey_800</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 07012d8..d89cc96 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5499,6 +5499,10 @@
     // OS: P
     SETTINGS_ZONE_PICKER_FIXED_OFFSET = 1357;
 
+    // Action: notification shade > manage notifications
+    // OS: P
+    ACTION_MANAGE_NOTIFICATIONS = 1358;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/backup/java/com/android/server/backup/transport/TransportStats.java b/services/backup/java/com/android/server/backup/transport/TransportStats.java
index 4bef81a..bd84782 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportStats.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportStats.java
@@ -32,9 +32,12 @@
 
     void registerConnectionTime(ComponentName transportComponent, long timeMs) {
         synchronized (mStatsLock) {
-            mTransportStats
-                    .computeIfAbsent(transportComponent, name -> new Stats())
-                    .register(timeMs);
+            Stats stats = mTransportStats.get(transportComponent);
+            if (stats == null) {
+                stats = new Stats();
+                mTransportStats.put(transportComponent, stats);
+            }
+            stats.register(timeMs);
         }
     }
 
@@ -71,52 +74,49 @@
     private static void dumpStats(PrintWriter pw, String prefix, Stats stats) {
         pw.println(
                 String.format(
-                        Locale.US, "%sAverage connection time: %.2f ms", prefix, stats.mAverage));
-        pw.println(String.format(Locale.US, "%sMax connection time: %d ms", prefix, stats.mMax));
-        pw.println(String.format(Locale.US, "%sMin connection time: %d ms", prefix, stats.mMin));
-        pw.println(String.format(Locale.US, "%sNumber of connections: %d ", prefix, stats.mN));
+                        Locale.US, "%sAverage connection time: %.2f ms", prefix, stats.average));
+        pw.println(String.format(Locale.US, "%sMax connection time: %d ms", prefix, stats.max));
+        pw.println(String.format(Locale.US, "%sMin connection time: %d ms", prefix, stats.min));
+        pw.println(String.format(Locale.US, "%sNumber of connections: %d ", prefix, stats.n));
     }
 
     public static final class Stats {
         public static Stats merge(Stats a, Stats b) {
             return new Stats(
-                    a.mN + b.mN,
-                    (a.mAverage * a.mN + b.mAverage * b.mN) / (a.mN + b.mN),
-                    Math.max(a.mMax, b.mMax),
-                    Math.min(a.mMin, b.mMin));
+                    a.n + b.n,
+                    (a.average * a.n + b.average * b.n) / (a.n + b.n),
+                    Math.max(a.max, b.max),
+                    Math.min(a.min, b.min));
         }
 
-        public int mN;
-        public double mAverage;
-        public long mMax;
-        public long mMin;
+        public int n;
+        public double average;
+        public long max;
+        public long min;
 
         public Stats() {
-            mN = 0;
-            mAverage = 0;
-            mMax = 0;
-            mMin = Long.MAX_VALUE;
-        }
-
-        private Stats(Stats original) {
-            mN = original.mN;
-            mAverage = original.mAverage;
-            mMax = original.mMax;
-            mMin = original.mMin;
+            n = 0;
+            average = 0;
+            max = 0;
+            min = Long.MAX_VALUE;
         }
 
         private Stats(int n, double average, long max, long min) {
-            mN = n;
-            mAverage = average;
-            mMax = max;
-            mMin = min;
+            this.n = n;
+            this.average = average;
+            this.max = max;
+            this.min = min;
+        }
+
+        private Stats(Stats original) {
+            this(original.n, original.average, original.max, original.min);
         }
 
         private void register(long sample) {
-            mAverage = (mAverage * mN + sample) / (mN + 1);
-            mN++;
-            mMax = Math.max(mMax, sample);
-            mMin = Math.min(mMin, sample);
+            average = (average * n + sample) / (n + 1);
+            n++;
+            max = Math.max(max, sample);
+            min = Math.min(min, sample);
         }
     }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 3927ebd..62e82a0 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -394,8 +394,10 @@
                 + " callback.asBinder=" + callback.asBinder());
         }
 
+        // TODO(b/70041899): Find a way to make this work for carrier-privileged callers.
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mContext, callingPackage, "addOnSubscriptionsChangedListener")) {
+                mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
+                "addOnSubscriptionsChangedListener")) {
             return;
         }
 
@@ -686,8 +688,9 @@
 
     private boolean canReadPhoneState(String callingPackage, String message) {
         try {
+            // TODO(b/70041899): Find a way to make this work for carrier-privileged callers.
             return TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                    mContext, callingPackage, message);
+                    mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message);
         } catch (SecurityException e) {
             return false;
         }
@@ -1735,8 +1738,9 @@
         }
 
         if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
-            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                    mContext, callingPackage, message)) {
+            // TODO(b/70041899): Find a way to make this work for carrier-privileged callers.
+            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message)) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c1c68e8..96633da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5401,11 +5401,12 @@
 
         final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
-                        SafeActivityOptions.fromBundle(bOptions));
+                        safeOptions);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 59f027a..8c49472 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1621,12 +1621,6 @@
             return;
         }
 
-        if (isState(DESTROYED) || (state != DESTROYED && isState(DESTROYING))) {
-            // We cannot move backwards from destroyed and destroying states.
-            throw new IllegalArgumentException("cannot move back states once destroying"
-                    + "current:" + mState + " requested:" + state);
-        }
-
         final ActivityState prev = mState;
         mState = state;
 
@@ -1641,23 +1635,6 @@
         if (parent != null) {
             parent.onActivityStateChanged(this, state, reason);
         }
-
-        if (isState(DESTROYING, DESTROYED)) {
-            makeFinishingLocked();
-
-            // When moving to the destroyed state, immediately destroy the activity in the
-            // associated stack. Most paths for finishing an activity will handle an activity's path
-            // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities.
-            // However, moving to the destroyed state directly (as in the case of an app dying) and
-            // marking it as finished will lead to cleanup steps that will prevent later handling
-            // from happening.
-            if (isState(DESTROYED)) {
-                final ActivityStack stack = getStack();
-                if (stack != null) {
-                    stack.activityDestroyedLocked(this, reason);
-                }
-            }
-        }
     }
 
     ActivityState getState() {
@@ -1750,8 +1727,11 @@
             // this when there is an activity waiting to become translucent as the extra binder
             // calls will lead to noticeable jank. A later call to
             // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
-            // paused state.
-            if (isState(STOPPED, STOPPING) && stack.mTranslucentActivityWaiting == null) {
+            // paused state. We also avoid doing this for the activity the stack supervisor
+            // considers the resumed activity, as normal means will bring the activity from STOPPED
+            // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
+            if (isState(STOPPED, STOPPING) && stack.mTranslucentActivityWaiting == null
+                    && mStackSupervisor.getResumedActivityLocked() != this) {
                 // Capture reason before state change
                 final String reason = getLifecycleDescription("makeVisibleIfNeeded");
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2d7520e..c00fc6a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3811,14 +3811,6 @@
         final ActivityState prevState = r.getState();
         if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
 
-        // We are already destroying / have already destroyed the activity. Do not continue to
-        // modify it. Note that we do not use ActivityRecord#finishing here as finishing is not
-        // indicative of destruction (though destruction is indicative of finishing) as finishing
-        // can be delayed below.
-        if (r.isState(DESTROYING, DESTROYED)) {
-            return null;
-        }
-
         r.setState(FINISHING, "finishCurrentActivityLocked");
         final boolean finishingActivityInNonFocusedStack
                 = r.getStack() != mStackSupervisor.getFocusedStack()
@@ -4037,26 +4029,16 @@
      * state to destroy so only the cleanup here is needed.
      *
      * Note: Call before #removeActivityFromHistoryLocked.
-     *
-     * @param r             The {@link ActivityRecord} to cleanup.
-     * @param cleanServices Whether services bound to the {@link ActivityRecord} should also be
-     *                      cleaned up.
-     * @param destroy       Whether the {@link ActivityRecord} should be destroyed.
-     * @param clearProcess  Whether the client process should be cleared.
      */
-    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean destroy,
-            boolean clearProcess) {
+    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
         onActivityRemovedFromStack(r);
 
         r.deferRelaunchUntilPaused = false;
         r.frozenBeforeDestroy = false;
 
-        if (destroy) {
+        if (setState) {
             if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
             r.setState(DESTROYED, "cleanupActivityLocked");
-        }
-
-        if (clearProcess) {
             if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
             r.app = null;
         }
@@ -4271,7 +4253,7 @@
                         + ", app=" + (r.app != null ? r.app.processName : "(null)"));
 
         if (r.isState(DESTROYING, DESTROYED)) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already finishing."
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
                     + "skipping request with reason:" + reason);
             return false;
         }
@@ -4282,8 +4264,7 @@
 
         boolean removedFromHistory = false;
 
-        cleanUpActivityLocked(r, false /* cleanServices */, false /* destroy */,
-                false /*clearProcess*/);
+        cleanUpActivityLocked(r, false, false);
 
         final boolean hadApp = r.app != null;
 
@@ -4380,6 +4361,10 @@
         }
     }
 
+    /**
+     * This method is to only be called from the client via binder when the activity is destroyed
+     * AND finished.
+     */
     final void activityDestroyedLocked(ActivityRecord record, String reason) {
         if (record != null) {
             mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
@@ -4389,8 +4374,7 @@
 
         if (isInStackLocked(record) != null) {
             if (record.isState(DESTROYING, DESTROYED)) {
-                cleanUpActivityLocked(record, true /* cleanServices */, false /* destroy */,
-                        false /*clearProcess*/);
+                cleanUpActivityLocked(record, true, false);
                 removeActivityFromHistoryLocked(record, reason);
             }
         }
@@ -4499,8 +4483,7 @@
                             r.icicle = null;
                         }
                     }
-                    cleanUpActivityLocked(r, true /* cleanServices */, remove /* destroy */,
-                            true /*clearProcess*/);
+                    cleanUpActivityLocked(r, true, true);
                     if (remove) {
                         removeActivityFromHistoryLocked(r, "appDied");
                     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 869c635..abd24e1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3376,10 +3376,11 @@
                 } else {
                     stack.awakeFromSleepingLocked();
                     if (isFocusedStack(stack)
-                            && !mKeyguardController.isKeyguardActive(display.mDisplayId)) {
-                        // If there is no keyguard on this display - resume immediately. Otherwise
-                        // we'll wait for keyguard visibility callback and resume while ensuring
-                        // activities visibility
+                            && !mKeyguardController.isKeyguardShowing(display.mDisplayId)) {
+                        // If the keyguard is unlocked - resume immediately.
+                        // It is possible that the display will not be awake at the time we
+                        // process the keyguard going away, which can happen before the sleep token
+                        // is released. As a result, it is important we resume the activity here.
                         resumeFocusedStackTopActivityLocked();
                     }
                 }
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 6b8b380..72882de 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -86,16 +86,8 @@
      *         display, false otherwise
      */
     boolean isKeyguardShowing(int displayId) {
-        return isKeyguardActive(displayId) && !mKeyguardGoingAway;
-    }
-
-    /**
-     * @return true if Keyguard is showing and not occluded. We ignore whether it is going away or
-     *         not here.
-     */
-    boolean isKeyguardActive(int displayId) {
-        return mKeyguardShowing && (displayId == DEFAULT_DISPLAY ? !mOccluded
-                : displayId == mSecondaryDisplayShowing);
+        return mKeyguardShowing && !mKeyguardGoingAway &&
+                (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index f1a806b..4f31e53 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -102,9 +102,12 @@
 
 
     /**
-     * There are only 2 possible callbacks.
+     * There are only 3 possible callbacks.
      *
-     * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY].
+     * mNetdEventCallbackList[CALLBACK_CALLER_CONNECTIVITY_SERVICE]
+     * Callback registered/unregistered by ConnectivityService.
+     *
+     * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY]
      * Callback registered/unregistered when logging is being enabled/disabled in DPM
      * by the device owner. It's DevicePolicyManager's responsibility to ensure that.
      *
@@ -113,6 +116,7 @@
      */
     @GuardedBy("this")
     private static final int[] ALLOWED_CALLBACK_TYPES = {
+        INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
         INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY,
         INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST
     };
@@ -212,6 +216,19 @@
     @Override
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
+    public synchronized void onPrivateDnsValidationEvent(int netId,
+            String ipAddress, String hostname, boolean validated)
+            throws RemoteException {
+        for (INetdEventCallback callback : mNetdEventCallbackList) {
+            if (callback != null) {
+                callback.onPrivateDnsValidationEvent(netId, ipAddress, hostname, validated);
+            }
+        }
+    }
+
+    @Override
+    // Called concurrently by multiple binder threads.
+    // This method must not block or perform long-running operations.
     public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
             int port, int uid) throws RemoteException {
         long timestamp = System.currentTimeMillis();
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index c3259c3..ac85484 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -26,8 +26,10 @@
 import android.app.ActivityManager.RunningAppProcessInfo;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
+import android.app.IActivityManager;
 import android.app.PendingIntent;
 import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -137,6 +139,7 @@
     @GuardedBy("this")
     private IBiometricsFingerprint mDaemon;
     private IStatusBarService mStatusBarService;
+    private final IActivityManager mActivityManager;
     private final PowerManager mPowerManager;
     private final AlarmManager mAlarmManager;
     private final UserManager mUserManager;
@@ -215,6 +218,30 @@
         }
     };
 
+    private final TaskStackListener mTaskStackListener = new TaskStackListener() {
+        @Override
+        public void onTaskStackChanged() {
+            try {
+                if (!(mCurrentClient instanceof AuthenticationClient)) {
+                    return;
+                }
+                if (isKeyguard(mCurrentClient.getOwnerString())) {
+                    return; // Keyguard is always allowed
+                }
+                List<ActivityManager.RunningTaskInfo> runningTasks = mActivityManager.getTasks(1);
+                if (!runningTasks.isEmpty()) {
+                    if (runningTasks.get(0).topActivity.getPackageName()
+                            != mCurrentClient.getOwnerString()) {
+                        mCurrentClient.stop(false /* initiatedByClient */);
+                        Slog.e(TAG, "Stopping background authentication");
+                    }
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to get running tasks", e);
+            }
+        }
+    };
+
     public FingerprintService(Context context) {
         super(context);
         mContext = context;
@@ -230,6 +257,13 @@
         mFailedAttempts = new SparseIntArray();
         mStatusBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mActivityManager = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
+                .getService();
+        try {
+            mActivityManager.registerTaskStackListener(mTaskStackListener);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Could not register task stack listener", e);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index aa1f7d95..c1593a7 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -874,7 +874,14 @@
             } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA) {
                 applyRestrictions(muteMedia || muteEverything, usage);
             } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_SYSTEM) {
-                applyRestrictions(muteSystem || muteEverything, usage);
+                if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) {
+                    // normally DND will only restrict touch sounds, not haptic feedback/vibrations
+                    applyRestrictions(muteSystem || muteEverything, usage,
+                            AppOpsManager.OP_PLAY_AUDIO);
+                    applyRestrictions(false, usage, AppOpsManager.OP_VIBRATE);
+                } else {
+                    applyRestrictions(muteSystem || muteEverything, usage);
+                }
             } else {
                 applyRestrictions(muteEverything, usage);
             }
@@ -883,18 +890,22 @@
 
 
     @VisibleForTesting
-    protected void applyRestrictions(boolean mute, int usage) {
+    protected void applyRestrictions(boolean mute, int usage, int code) {
         final String[] exceptionPackages = null; // none (for now)
 
-        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
-                mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
-                exceptionPackages);
-        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, usage,
+        mAppOps.setRestriction(code, usage,
                 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                 exceptionPackages);
     }
 
     @VisibleForTesting
+    protected void applyRestrictions(boolean mute, int usage) {
+        applyRestrictions(mute, usage, AppOpsManager.OP_VIBRATE);
+        applyRestrictions(mute, usage, AppOpsManager.OP_PLAY_AUDIO);
+    }
+
+
+    @VisibleForTesting
     protected void applyZenToRingerMode() {
         if (mAudioManager == null) return;
         // force the ringer mode into compliance
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 938edb0..c14beef 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1755,7 +1755,6 @@
     }
 
     void showGlobalActionsInternal() {
-        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
         if (mGlobalActions == null) {
             mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
         }
@@ -2680,8 +2679,9 @@
                 attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
                 break;
             case TYPE_DREAM:
-                // Dreams don't have an app window token and can thus not be letterboxed.
-                // Hence always let them extend under the cutout.
+            case TYPE_WALLPAPER:
+                // Dreams and wallpapers don't have an app window token and can thus not be
+                // letterboxed. Hence always let them extend under the cutout.
                 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
                 break;
             case TYPE_STATUS_BAR:
@@ -4757,7 +4757,7 @@
             // It's a system nav bar or a portrait screen; nav bar goes on bottom.
             final int top = cutoutSafeUnrestricted.bottom
                     - getNavigationBarHeight(rotation, uiMode);
-            mTmpNavigationFrame.set(0, top, displayWidth, cutoutSafeUnrestricted.bottom);
+            mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
             displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
             if (transientNavBarShowing) {
                 mNavigationBarController.setBarShowingLw(true);
@@ -4780,7 +4780,7 @@
             // Landscape screen; nav bar goes to the right.
             final int left = cutoutSafeUnrestricted.right
                     - getNavigationBarWidth(rotation, uiMode);
-            mTmpNavigationFrame.set(left, 0, cutoutSafeUnrestricted.right, displayHeight);
+            mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
             displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
             if (transientNavBarShowing) {
                 mNavigationBarController.setBarShowingLw(true);
@@ -4803,7 +4803,7 @@
             // Seascape screen; nav bar goes to the left.
             final int right = cutoutSafeUnrestricted.left
                     + getNavigationBarWidth(rotation, uiMode);
-            mTmpNavigationFrame.set(cutoutSafeUnrestricted.left, 0, right, displayHeight);
+            mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
             displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
             if (transientNavBarShowing) {
                 mNavigationBarController.setBarShowingLw(true);
@@ -4832,8 +4832,9 @@
         mStatusBarLayer = mNavigationBar.getSurfaceLayer();
         // And compute the final frame.
         mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
-                mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
-                mTmpNavigationFrame, mTmpNavigationFrame, displayFrames.mDisplayCutout);
+                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
+                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
+                displayFrames.mDisplayCutout);
         if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
         return mNavigationBarController.checkHiddenLw();
     }
@@ -4991,8 +4992,7 @@
             df.set(displayFrames.mDock);
             pf.set(displayFrames.mDock);
             // IM dock windows layout below the nav bar...
-            pf.bottom = df.bottom = of.bottom = Math.min(displayFrames.mUnrestricted.bottom,
-                    displayFrames.mDisplayCutoutSafe.bottom);
+            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
             // ...with content insets above the nav bar
             cf.bottom = vf.bottom = displayFrames.mStable.bottom;
             if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
@@ -5293,27 +5293,48 @@
 
         final int cutoutMode = attrs.layoutInDisplayCutoutMode;
         final boolean attachedInParent = attached != null && !layoutInScreen;
+        final boolean requestedHideNavigation =
+                (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
-            final Rect displayCutoutSafeExceptMaybeTop = mTmpRect;
-            displayCutoutSafeExceptMaybeTop.set(displayFrames.mDisplayCutoutSafe);
+            final Rect displayCutoutSafeExceptMaybeBars = mTmpRect;
+            displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
             if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
                     && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
                 // At the top we have the status bar, so apps that are
                 // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
                 // already expect that there's an inset there and we don't need to exclude
                 // the window from that area.
-                displayCutoutSafeExceptMaybeTop.top = Integer.MIN_VALUE;
+                displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+            }
+            if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
+                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                // Same for the navigation bar.
+                switch (mNavigationBarPosition) {
+                    case NAV_BAR_BOTTOM:
+                        displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                        break;
+                    case NAV_BAR_RIGHT:
+                        displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                        break;
+                    case NAV_BAR_LEFT:
+                        displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                        break;
+                }
+            }
+            if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+                // The IME can always extend under the bottom cutout if the navbar is there.
+                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
             }
             // Windows that are attached to a parent and laid out in said parent are already
             // avoidingthe cutout according to that parent and don't need to be further constrained.
             if (!attachedInParent) {
-                pf.intersectUnchecked(displayCutoutSafeExceptMaybeTop);
+                pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
             }
-            // Make sure that NO_LIMITS windows clipped to the display don't extend into the display
-            // don't extend under the cutout.
-            df.intersectUnchecked(displayCutoutSafeExceptMaybeTop);
+            // Make sure that NO_LIMITS windows clipped to the display don't extend under the
+            // cutout.
+            df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
         }
 
         // Content should never appear in the cutout.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b729b6a..285532a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -720,9 +720,12 @@
     private void playChargingStartedSound() {
         final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
+        final boolean dndOff = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+                == Settings.Global.ZEN_MODE_OFF;
         final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.CHARGING_STARTED_SOUND);
-        if (enabled && soundPath != null) {
+        if (enabled && dndOff && soundPath != null) {
             final Uri soundUri = Uri.parse("file://" + soundPath);
             if (soundUri != null) {
                 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 64f77a2..00e3050 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -17,13 +17,15 @@
 package com.android.server.wm;
 
 import android.annotation.ColorInt;
-import android.graphics.Point;
+import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.animation.Animation;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
+import java.io.PrintWriter;
+
 /**
  * Interface that describes an animation and bridges the animation start to the component
  * responsible for running the animation.
@@ -83,4 +85,14 @@
      * @return the desired start time of the status bar transition, in uptime millis
      */
     long getStatusBarTransitionsStartTime();
+
+    void dump(PrintWriter pw, String prefix);
+
+    default void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        writeToProto(proto);
+        proto.end(token);
+    }
+
+    void writeToProto(ProtoOutputStream proto);
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 40f772a..1170148 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -379,6 +379,8 @@
 
                 if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
                 wtoken.mAppStopped = false;
+
+                mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
             }
 
             // If we are preparing an app transition, then delay changing
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2672337..b2f153a 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -187,6 +187,7 @@
     StartingSurface startingSurface;
     boolean startingDisplayed;
     boolean startingMoved;
+
     // True if the hidden state of this token was forced to false due to a transferred starting
     // window.
     private boolean mHiddenSetFromTransferredStartingWindow;
@@ -1136,6 +1137,25 @@
         stopFreezingScreen(true, true);
     }
 
+    /**
+     * Tries to transfer the starting window from a token that's above ourselves in the task but
+     * not visible anymore. This is a common scenario apps use: Trampoline activity T start main
+     * activity M in the same task. Now, when reopening the task, T starts on top of M but then
+     * immediately finishes after, so we have to transfer T to M.
+     */
+    void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
+        final Task task = getTask();
+        for (int i = task.mChildren.size() - 1; i >= 0; i--) {
+            final AppWindowToken fromToken = task.mChildren.get(i);
+            if (fromToken == this) {
+                return;
+            }
+            if (fromToken.hiddenRequested && transferStartingWindow(fromToken.token)) {
+                return;
+            }
+        }
+    }
+
     boolean transferStartingWindow(IBinder transferFrom) {
         final AppWindowToken fromToken = getDisplayContent().getAppWindowToken(transferFrom);
         if (fromToken == null) {
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index a180a3a..5c62987 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -16,11 +16,19 @@
 
 package com.android.server.wm;
 
-import android.view.SurfaceControl;
+import static com.android.server.wm.proto.AlphaAnimationSpecProto.DURATION;
+import static com.android.server.wm.proto.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.proto.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.proto.AnimationSpecProto.ALPHA;
+
 import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
+
 /**
  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
@@ -334,5 +342,21 @@
                     + mFromAlpha;
             t.setAlpha(sc, alpha);
         }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
+            pw.print(" to="); pw.print(mToAlpha);
+            pw.print(" duration="); pw.println(mDuration);
+        }
+
+        @Override
+        public void writeToProtoInner(ProtoOutputStream proto) {
+            final long token = proto.start(ALPHA);
+            proto.write(FROM, mFromAlpha);
+            proto.write(TO, mToAlpha);
+            proto.write(DURATION, mDuration);
+            proto.end(token);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 1b41cb8..3f1fde9 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -16,12 +16,18 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.proto.AnimationAdapterProto.LOCAL;
+import static com.android.server.wm.proto.LocalAnimationAdapterProto.ANIMATION_SPEC;
+
 import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
+import java.io.PrintWriter;
+
 /**
  * Animation that can be executed without holding the window manager lock. See
  * {@link SurfaceAnimationRunner}.
@@ -74,6 +80,18 @@
         return mSpec.calculateStatusBarTransitionStartTime();
     }
 
+    @Override
+    public void dump(PrintWriter pw, String prefix) {
+        mSpec.dump(pw, prefix);
+    }
+
+    @Override
+    public void writeToProto(ProtoOutputStream proto) {
+        final long token = proto.start(LOCAL);
+        mSpec.writeToProto(proto, ANIMATION_SPEC);
+        proto.end(token);
+    }
+
     /**
      * Describes how to apply an animation.
      */
@@ -127,5 +145,15 @@
         default boolean canSkipFirstFrame() {
             return false;
         }
+
+        void dump(PrintWriter pw, String prefix);
+
+        default void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            writeToProtoInner(proto);
+            proto.end(token);
+        }
+
+        void writeToProtoInner(ProtoOutputStream proto);
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 39b886d..7392e80 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -23,6 +23,8 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.proto.AnimationAdapterProto.REMOTE;
 
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.WindowConfiguration;
@@ -33,18 +35,21 @@
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Slog;
+import android.util.Slog;import android.util.proto.ProtoOutputStream;
 import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
 import com.google.android.collect.Sets;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
-
 /**
  * Controls a single instance of the remote driven recents animation. In particular, this allows
  * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
@@ -235,11 +240,16 @@
             return;
         }
         try {
-            final RemoteAnimationTarget[] appAnimations =
-                    new RemoteAnimationTarget[mPendingAnimations.size()];
+            final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
             for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-                appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
+                final RemoteAnimationTarget target =
+                        mPendingAnimations.get(i).createRemoteAnimationApp();
+                if (target != null) {
+                    appAnimations.add(target);
+                }
             }
+            final RemoteAnimationTarget[] appTargets = appAnimations.toArray(
+                    new RemoteAnimationTarget[appAnimations.size()]);
             mPendingStart = false;
 
             final Rect minimizedHomeBounds =
@@ -248,7 +258,7 @@
             final Rect contentInsets =
                     mHomeAppToken != null && mHomeAppToken.findMainWindow() != null
                             ? mHomeAppToken.findMainWindow().mContentInsets : null;
-            mRunner.onAnimationStart_New(mController, appAnimations, contentInsets,
+            mRunner.onAnimationStart_New(mController, appTargets, contentInsets,
                     minimizedHomeBounds);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to start recents animation", e);
@@ -348,6 +358,7 @@
         private SurfaceControl mCapturedLeash;
         private OnAnimationFinishedCallback mCapturedFinishCallback;
         private final boolean mIsRecentTaskInvisible;
+        private RemoteAnimationTarget mTarget;
 
         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
             mTask = task;
@@ -361,10 +372,14 @@
             container.getRelativePosition(position);
             container.getBounds(bounds);
             final WindowState mainWindow = mTask.getTopVisibleAppMainWindow();
-            return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
+            if (mainWindow == null) {
+                return null;
+            }
+            mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
                     !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
                     mainWindow.mContentInsets, mTask.getPrefixOrderIndex(), position, bounds,
                     mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
+            return mTarget;
         }
 
         @Override
@@ -403,6 +418,26 @@
         public long getStatusBarTransitionsStartTime() {
             return SystemClock.uptimeMillis();
         }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix); pw.println("task=" + mTask);
+            if (mTarget != null) {
+                pw.print(prefix); pw.println("Target:");
+                mTarget.dump(pw, prefix + "  ");
+            } else {
+                pw.print(prefix); pw.println("Target: null");
+            }
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream proto) {
+            final long token = proto.start(REMOTE);
+            if (mTarget != null) {
+                mTarget.writeToProto(proto, TARGET);
+            }
+            proto.end(token);
+        }
     }
 
     public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 169d65e..d645110 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,8 +16,11 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.proto.RemoteAnimationAdapterWrapperProto.TARGET;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -25,16 +28,18 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationFinishedCallback.Stub;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
+import com.android.internal.util.FastPrintWriter;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
-import java.lang.ref.WeakReference;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 
 /**
@@ -104,6 +109,20 @@
             }
         });
         sendRunningRemoteAnimation(true);
+        if (DEBUG_APP_TRANSITIONS) {
+            writeStartDebugStatement();
+        }
+    }
+
+    private void writeStartDebugStatement() {
+        Slog.i(TAG, "Starting remote animation");
+        final StringWriter sw = new StringWriter();
+        final FastPrintWriter pw = new FastPrintWriter(sw);
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            mPendingAnimations.get(i).dump(pw, "");
+        }
+        pw.close();
+        Slog.i(TAG, sw.toString());
     }
 
     private RemoteAnimationTarget[] createAnimations() {
@@ -133,6 +152,7 @@
             }
         }
         sendRunningRemoteAnimation(false);
+        if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
     }
 
     private void invokeAnimationCancelled() {
@@ -193,6 +213,7 @@
         private OnAnimationFinishedCallback mCapturedFinishCallback;
         private final Point mPosition = new Point();
         private final Rect mStackBounds = new Rect();
+        private RemoteAnimationTarget mTarget;
 
         RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
                 Rect stackBounds) {
@@ -210,11 +231,12 @@
             if (mainWindow == null) {
                 return null;
             }
-            return new RemoteAnimationTarget(task.mTaskId, getMode(),
+            mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
                     mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
                     mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
                     task.getWindowConfiguration(), false /*isNotInRecents*/);
+            return mTarget;
         }
 
         private int getMode() {
@@ -275,5 +297,25 @@
             return SystemClock.uptimeMillis()
                     + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
         }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
+            if (mTarget != null) {
+                pw.print(prefix); pw.println("Target:");
+                mTarget.dump(pw, prefix + "  ");
+            } else {
+                pw.print(prefix); pw.println("Target: null");
+            }
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream proto) {
+            final long token = proto.start(REMOTE);
+            if (mTarget != null) {
+                mTarget.writeToProto(proto, TARGET);
+            }
+            proto.end(token);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 76f5396..c06caaf 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -313,7 +313,9 @@
      */
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        proto.write(ANIMATION_ADAPTER, mAnimation != null ? mAnimation.toString() : "null");
+        if (mAnimation != null) {
+            mAnimation.writeToProto(proto, ANIMATION_ADAPTER);
+        }
         if (mLeash != null){
             mLeash.writeToProto(proto, LEASH);
         }
@@ -322,8 +324,18 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("mAnimation="); pw.print(mAnimation);
-        pw.print(" mLeash="); pw.println(mLeash);
+        pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
+        if (mAnimationStartDelayed) {
+            pw.print(" mAnimationStartDelayed="); pw.println(mAnimationStartDelayed);
+        } else {
+            pw.println();
+        }
+        pw.print(prefix); pw.println("Animation:");
+        if (mAnimation != null) {
+            mAnimation.dump(pw, prefix + "  ");
+        } else {
+            pw.print(prefix); pw.println("null");
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 43fa3d5..a41eba8 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -19,10 +19,13 @@
 import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
+import static com.android.server.wm.proto.AnimationSpecProto.WINDOW;
+import static com.android.server.wm.proto.WindowAnimationSpecProto.ANIMATION;
 
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.animation.Animation;
@@ -33,6 +36,8 @@
 
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
+import java.io.PrintWriter;
+
 /**
  * Animation spec for regular window animations.
  */
@@ -129,6 +134,18 @@
         return mCanSkipFirstFrame;
     }
 
+    @Override
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.println(mAnimation);
+    }
+
+    @Override
+    public void writeToProtoInner(ProtoOutputStream proto) {
+        final long token = proto.start(WINDOW);
+        proto.write(ANIMATION, mAnimation.toString());
+        proto.end(token);
+    }
+
     /**
      * Tries to find a {@link TranslateAnimation} inside the {@code animation}.
      *
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 145aee9..297e067 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -113,6 +113,10 @@
 import static com.android.server.wm.proto.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.proto.IdentifierProto.TITLE;
 import static com.android.server.wm.proto.IdentifierProto.USER_ID;
+import static com.android.server.wm.proto.AnimationSpecProto.MOVE;
+import static com.android.server.wm.proto.MoveAnimationSpecProto.DURATION;
+import static com.android.server.wm.proto.MoveAnimationSpecProto.FROM;
+import static com.android.server.wm.proto.MoveAnimationSpecProto.TO;
 import static com.android.server.wm.proto.WindowStateProto.ANIMATING_EXIT;
 import static com.android.server.wm.proto.WindowStateProto.ANIMATOR;
 import static com.android.server.wm.proto.WindowStateProto.ATTRIBUTES;
@@ -694,6 +698,7 @@
         mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
         mWindowId = new WindowId(this);
         mAttrs.copyFrom(a);
+        mLastSurfaceInsets.set(mAttrs.surfaceInsets);
         mViewVisibility = viewVisibility;
         mPolicy = mService.mPolicy;
         mContext = mService.mContext;
@@ -4723,5 +4728,21 @@
             t.setPosition(leash, mFrom.x + (mTo.x - mFrom.x) * v,
                     mFrom.y + (mTo.y - mFrom.y) * v);
         }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix); pw.print("from="); pw.print(mFrom);
+            pw.print(" to="); pw.print(mTo);
+            pw.print(" duration="); pw.println(mDuration);
+        }
+
+        @Override
+        public void writeToProtoInner(ProtoOutputStream proto) {
+            final long token = proto.start(MOVE);
+            mFrom.writeToProto(proto, FROM);
+            mTo.writeToProto(proto, TO);
+            proto.write(DURATION, mDuration);
+            proto.end(token);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4179590..40ee552 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -559,7 +559,8 @@
                         + wtoken.allDrawn + " startingDisplayed="
                         + wtoken.startingDisplayed + " startingMoved="
                         + wtoken.startingMoved + " isRelaunching()="
-                        + wtoken.isRelaunching());
+                        + wtoken.isRelaunching() + " startingWindow="
+                        + wtoken.startingWindow);
 
 
                 final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java b/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java
new file mode 100644
index 0000000..322db85c
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.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.backup.transport;
+
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.transport.TransportStats.Stats;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class TransportStatsTest {
+    private static final double TOLERANCE = 0.0001;
+
+    private TransportStats mTransportStats;
+    private ComponentName mTransportComponent1;
+    private ComponentName mTransportComponent2;
+
+    @Before
+    public void setUp() throws Exception {
+        mTransportStats = new TransportStats();
+        mTransportComponent1 = backupTransport().getTransportComponent();
+        mTransportComponent2 = d2dTransport().getTransportComponent();
+    }
+
+    @Test
+    public void testRegisterConnectionTime() {
+        mTransportStats.registerConnectionTime(mTransportComponent1, 50L);
+
+        Stats stats = mTransportStats.getStatsForTransport(mTransportComponent1);
+        assertThat(stats.average).isWithin(TOLERANCE).of(50);
+        assertThat(stats.max).isEqualTo(50L);
+        assertThat(stats.min).isEqualTo(50L);
+        assertThat(stats.n).isEqualTo(1);
+    }
+
+    @Test
+    public void testRegisterConnectionTime_whenHasAlreadyOneSample() {
+        mTransportStats.registerConnectionTime(mTransportComponent1, 50L);
+
+        mTransportStats.registerConnectionTime(mTransportComponent1, 100L);
+
+        Stats stats = mTransportStats.getStatsForTransport(mTransportComponent1);
+        assertThat(stats.average).isWithin(TOLERANCE).of(75);
+        assertThat(stats.max).isEqualTo(100L);
+        assertThat(stats.min).isEqualTo(50L);
+        assertThat(stats.n).isEqualTo(2);
+    }
+
+    @Test
+    public void testGetStatsForTransport() {
+        mTransportStats.registerConnectionTime(mTransportComponent1, 10L);
+        mTransportStats.registerConnectionTime(mTransportComponent2, 20L);
+
+        Stats stats = mTransportStats.getStatsForTransport(mTransportComponent1);
+
+        assertThat(stats.average).isWithin(TOLERANCE).of(10);
+        assertThat(stats.max).isEqualTo(10L);
+        assertThat(stats.min).isEqualTo(10L);
+        assertThat(stats.n).isEqualTo(1);
+    }
+
+    @Test
+    public void testMerge() {
+        mTransportStats.registerConnectionTime(mTransportComponent1, 10L);
+        mTransportStats.registerConnectionTime(mTransportComponent2, 20L);
+        Stats stats1 = mTransportStats.getStatsForTransport(mTransportComponent1);
+        Stats stats2 = mTransportStats.getStatsForTransport(mTransportComponent2);
+
+        Stats stats = Stats.merge(stats1, stats2);
+
+        assertThat(stats.average).isWithin(TOLERANCE).of(15);
+        assertThat(stats.max).isEqualTo(20L);
+        assertThat(stats.min).isEqualTo(10L);
+        assertThat(stats.n).isEqualTo(2);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 5b1f5c1..03e870a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -123,12 +123,20 @@
             }
             return null;
         }).when(mActivity.app.thread).scheduleTransaction(any());
+
         mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
 
+        // The activity is in the focused stack so it should not move to paused.
         mActivity.makeVisibleIfNeeded(null /* starting */);
+        assertTrue(mActivity.isState(STOPPED));
+        assertFalse(pauseFound.value);
 
+        // Clear focused stack
+        mActivity.mStackSupervisor.mFocusedStack = null;
+
+        // In the unfocused stack, the activity should move to paused.
+        mActivity.makeVisibleIfNeeded(null /* starting */);
         assertTrue(mActivity.isState(PAUSING));
-
         assertTrue(pauseFound.value);
 
         // Make sure that the state does not change for current non-stopping states.
@@ -214,35 +222,4 @@
         verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
                 anyInt(), anyInt(), eq(record.info));
     }
-
-    @Test
-    public void testFinishingAfterDestroying() throws Exception {
-        assertFalse(mActivity.finishing);
-        mActivity.setState(DESTROYING, "testFinishingAfterDestroying");
-        assertTrue(mActivity.isState(DESTROYING));
-        assertTrue(mActivity.finishing);
-    }
-
-    @Test
-    public void testFinishingAfterDestroyed() throws Exception {
-        assertFalse(mActivity.finishing);
-        mActivity.setState(DESTROYED, "testFinishingAfterDestroyed");
-        assertTrue(mActivity.isState(DESTROYED));
-        assertTrue(mActivity.finishing);
-    }
-
-    @Test
-    public void testSetInvalidState() throws Exception {
-        mActivity.setState(DESTROYED, "testInvalidState");
-
-        boolean exceptionEncountered = false;
-
-        try {
-            mActivity.setState(FINISHING, "testInvalidState");
-        } catch (IllegalArgumentException e) {
-            exceptionEncountered = true;
-        }
-
-        assertTrue(exceptionEncountered);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index bda68d1..f17bfa4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -479,28 +479,6 @@
     }
 
     @Test
-    public void testSuppressMultipleDestroy() throws Exception {
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
-        final ClientLifecycleManager lifecycleManager = mock(ClientLifecycleManager.class);
-        final ProcessRecord app = r.app;
-
-        // The mocked lifecycle manager must be set on the ActivityStackSupervisor's reference to
-        // the service rather than mService as mService is a spy and setting the value will not
-        // propagate as ActivityManagerService hands its own reference to the
-        // ActivityStackSupervisor during construction.
-        ((TestActivityManagerService) mSupervisor.mService).setLifecycleManager(lifecycleManager);
-
-        mStack.destroyActivityLocked(r, true, "first invocation");
-        verify(lifecycleManager, times(1)).scheduleTransaction(eq(app.thread),
-                eq(r.appToken), any(DestroyActivityItem.class));
-        assertTrue(r.isState(DESTROYED, DESTROYING));
-
-        mStack.destroyActivityLocked(r, true, "second invocation");
-        verify(lifecycleManager, times(1)).scheduleTransaction(eq(app.thread),
-                eq(r.appToken), any(DestroyActivityItem.class));
-    }
-
-    @Test
     public void testFinishDisabledPackageActivities() throws Exception {
         final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build();
         final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask).build();
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 76e4e89..e0645b1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -20,11 +20,9 @@
 import org.junit.Test;
 
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.SecurityTest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.WindowManager;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -36,13 +34,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
-import java.util.function.Consumer;
+import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
 
 /**
  * Test class for {@link AppWindowContainerController}.
  *
- * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
+ * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
  */
 @SmallTest
 @Presubmit
@@ -176,6 +173,33 @@
     }
 
     @Test
+    public void testTryTransferStartingWindowFromHiddenAboveToken() throws Exception {
+
+        // Add two tasks on top of each other.
+        TestTaskWindowContainerController taskController =
+                new WindowTestUtils.TestTaskWindowContainerController(this);
+        final WindowTestUtils.TestAppWindowContainerController controllerTop =
+                createAppWindowController(taskController);
+        final WindowTestUtils.TestAppWindowContainerController controllerBottom =
+                createAppWindowController(taskController);
+
+        // Add a starting window.
+        controllerTop.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+
+        // Make the top one invisible, and try transfering the starting window from the top to the
+        // bottom one.
+        controllerTop.setVisibility(false, false);
+        controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+
+        // Assert that the bottom window now has the starting window.
+        assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent));
+        assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent));
+    }
+
+    @Test
     public void testReparent() throws Exception {
         final StackWindowController stackController =
             createStackControllerOnDisplay(mDisplayContent);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 9008803..be58fd2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -160,6 +161,26 @@
     }
 
     @Test
+    public void testTotalSilence() {
+        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+        mZenModeHelperSpy.applyRestrictions();
+
+        // Total silence will silence alarms, media and system noises (but not vibrations)
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_ALARM);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_MEDIA);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_GAME);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+                AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_VIBRATE);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_UNKNOWN);
+    }
+
+    @Test
     public void testAlarmsOnly_alarmMediaMuteNotApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
@@ -179,9 +200,9 @@
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
                 AudioAttributes.USAGE_GAME);
 
-        // Alarms only will silence system noises
+        // Alarms only will silence system noises (but not vibrations)
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
-                AudioAttributes.USAGE_ASSISTANCE_SONIFICATION);
+                AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
                 AudioAttributes.USAGE_UNKNOWN);
     }
@@ -228,6 +249,7 @@
     @Test
     public void testZenAllCannotBypass() {
         // Only audio attributes with SUPPRESIBLE_NEVER can bypass
+        // with special case USAGE_ASSISTANCE_SONIFICATION
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
         mZenModeHelperSpy.mConfig.allowMedia = false;
@@ -247,9 +269,17 @@
         mZenModeHelperSpy.applyRestrictions();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
-            boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
-                    != AudioAttributes.SUPPRESSIBLE_NEVER;
-            verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+            if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) {
+                // only mute audio, not vibrations
+                verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, usage,
+                        AppOpsManager.OP_PLAY_AUDIO);
+                verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, usage,
+                        AppOpsManager.OP_VIBRATE);
+            } else {
+                boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
+                        != AudioAttributes.SUPPRESSIBLE_NEVER;
+                verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+            }
         }
     }
 
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 7f43ee5..073c313 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -115,6 +115,8 @@
             telephony.stopNetworkScan(mSubId, mScanId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
+        } catch (RuntimeException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RuntimeException", ex);
         }
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4a61437..ef66ed7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -25,6 +25,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.BroadcastOptions;
@@ -42,7 +43,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.DisplayMetrics;
 
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -59,9 +59,6 @@
 /**
  * SubscriptionManager is the application interface to SubscriptionController
  * and provides information about the current Telephony Subscriptions.
- * <p>
- * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise
- * specified.
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 public class SubscriptionManager {
@@ -612,6 +609,8 @@
      * @param listener an instance of {@link OnSubscriptionsChangedListener} with
      *                 onSubscriptionsChanged overridden.
      */
+    // TODO(b/70041899): Find a way to extend this to carrier-privileged apps.
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
         String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         if (DBG) {
@@ -660,9 +659,15 @@
     /**
      * Get the active SubscriptionInfo with the input subId.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @param subId The unique SubscriptionInfo key in database.
      * @return SubscriptionInfo, maybe null if its not active.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
         if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
         if (!isValidSubscriptionId(subId)) {
@@ -716,9 +721,16 @@
 
     /**
      * Get the active SubscriptionInfo associated with the slotIndex
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @param slotIndex the slot which the subscription is inserted
      * @return SubscriptionInfo, maybe null if its not active
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
         if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
         if (!isValidSlotIndex(slotIndex)) {
@@ -770,6 +782,11 @@
      * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
      * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
+     * to the calling app are returned.
+     *
      * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
      * <ul>
      * <li>
@@ -786,6 +803,8 @@
      * </li>
      * </ul>
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
@@ -928,10 +947,18 @@
     }
 
     /**
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, the count will include
+     * only those subscriptions accessible to the caller.
+     *
      * @return the current number of active subscriptions. There is no guarantee the value
      * returned by this method will be the same as the length of the list returned by
      * {@link #getActiveSubscriptionInfoList}.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getActiveSubscriptionInfoCount() {
         int result = 0;
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 530eda1..7add893 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -1113,7 +1114,11 @@
      * Returns the software version number for the device, for example,
      * the IMEI/SV for GSM phones. Return null if the software version is
      * not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceSoftwareVersion() {
         return getDeviceSoftwareVersion(getSlotIndex());
@@ -1145,10 +1150,14 @@
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
      * or ESN for CDMA phones. Return null if device ID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId() {
         try {
@@ -1167,12 +1176,16 @@
      * Returns the unique device ID of a subscription, for example, the IMEI for
      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which deviceID is returned
      *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
@@ -1191,7 +1204,11 @@
     /**
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei() {
         return getImei(getSlotIndex());
@@ -1201,8 +1218,12 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which IMEI is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1219,7 +1240,11 @@
 
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid() {
         return getMeid(getSlotIndex());
@@ -1228,8 +1253,12 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which MEID is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1246,10 +1275,11 @@
 
     /**
      * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getNai() {
         return getNaiBySubscriberId(getSubId());
@@ -1750,6 +1780,7 @@
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      */
+    // TODO(b/73136824, b/70041899): Permit carrier-privileged callers as well.
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PersistableBundle getCarrierConfig() {
@@ -1948,6 +1979,9 @@
      * If this object has been created with {@link #createForSubscriptionId}, applies to the given
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return the network type
      *
      * @see #NETWORK_TYPE_UNKNOWN
@@ -1967,6 +2001,7 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
@@ -2001,7 +2036,11 @@
 
     /**
      * Returns the NETWORK_TYPE_xxxx for voice
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getVoiceNetworkType() {
         return getVoiceNetworkType(getSubId());
@@ -2587,7 +2626,11 @@
     /**
      * Returns the serial number of the SIM, if applicable. Return null if it is
      * unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSimSerialNumber() {
          return getSimSerialNumber(getSubId());
@@ -2712,7 +2755,11 @@
     /**
      * Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
      * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSubscriberId() {
         return getSubscriberId(getSubId());
@@ -2876,7 +2923,11 @@
     /**
      * Returns the Group Identifier Level1 for a GSM phone.
      * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getGroupIdLevel1() {
         try {
@@ -2917,9 +2968,15 @@
     /**
      * Returns the phone number string for line 1, for example, the MSISDN
      * for a GSM phone. Return null if it is unavailable.
-     * <p>
-     * The default SMS app can also use this.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_SMS,
@@ -2974,8 +3031,7 @@
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
@@ -2991,8 +3047,7 @@
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
@@ -3111,7 +3166,11 @@
 
     /**
      * Returns the voice mail number. Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailNumber() {
         return getVoiceMailNumber(getSubId());
@@ -3172,8 +3231,7 @@
     /**
      * Sets the voice mail number.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
@@ -3185,8 +3243,7 @@
     /**
      * Sets the voicemail number for the given subscriber.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param alphaTag The alpha tag to display.
@@ -3207,9 +3264,9 @@
     /**
      * Enables or disables the visual voicemail client for a phone account.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or
-     * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle the phone account to change the client state
      * @param enabled the new state of the client
@@ -3272,11 +3329,15 @@
      * to the TelephonyManager. Returns {@code null} when there is no package responsible for
      * processing visual voicemail for the subscription.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      * @see VisualVoicemailService
      */
     @Nullable
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVisualVoicemailPackageName() {
         try {
@@ -3519,15 +3580,14 @@
       * Sets the voice activation state
       *
       * <p>Requires Permission:
-      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-      * Or the calling app has carrier privileges.
+      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+      * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
       *
       * @param activationState The voice activation state
       * @see #SIM_ACTIVATION_STATE_UNKNOWN
       * @see #SIM_ACTIVATION_STATE_ACTIVATING
       * @see #SIM_ACTIVATION_STATE_ACTIVATED
       * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-      * @see #hasCarrierPrivileges
       * @hide
       */
     @SystemApi
@@ -3540,8 +3600,8 @@
      * Sets the voice activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param activationState The voice activation state of the given subscriber.
@@ -3549,7 +3609,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3567,8 +3626,8 @@
      * Sets the data activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param activationState The data activation state
      * @see #SIM_ACTIVATION_STATE_UNKNOWN
@@ -3576,7 +3635,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3589,8 +3647,8 @@
      * Sets the data activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param activationState The data activation state of the given subscriber.
@@ -3599,7 +3657,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3617,15 +3674,14 @@
      * Returns the voice activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return voiceActivationState
      * @see #SIM_ACTIVATION_STATE_UNKNOWN
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3638,8 +3694,8 @@
      * Returns the voice activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      *
@@ -3648,7 +3704,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3667,8 +3722,8 @@
      * Returns the data activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return dataActivationState for the given subscriber
      * @see #SIM_ACTIVATION_STATE_UNKNOWN
@@ -3676,7 +3731,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3689,8 +3743,8 @@
      * Returns the data activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      *
@@ -3700,7 +3754,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3748,7 +3801,11 @@
     /**
      * Retrieves the alphabetic identifier associated with the voice
      * mail number.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailAlphaTag() {
         return getVoiceMailAlphaTag(getSubId());
@@ -3777,9 +3834,8 @@
     }
 
     /**
-     * Send the special dialer code. The IPC caller must be the current default dialer or has
-     * carrier privileges.
-     * @see #hasCarrierPrivileges
+     * Send the special dialer code. The IPC caller must be the current default dialer or have
+     * carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param inputCode The special dialer code to send
      *
@@ -4303,8 +4359,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return an IccOpenLogicalChannelResponse object.
@@ -4321,8 +4377,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
@@ -4338,8 +4394,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param AID Application id. See ETSI 102.221 and 101.220.
@@ -4364,8 +4420,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param channel is the channel id to be closed as retruned by a successful
      *            iccOpenLogicalChannel.
@@ -4381,8 +4437,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param channel is the channel id to be closed as retruned by a successful
@@ -4407,8 +4463,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
@@ -4434,8 +4490,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param channel is the channel id to be closed as returned by a successful
@@ -4470,8 +4526,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param cla Class of the APDU command.
      * @param instruction Instruction of the APDU command.
@@ -4495,8 +4551,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param cla Class of the APDU command.
@@ -4527,8 +4583,8 @@
      * Returns the response APDU for a command APDU sent through SIM_IO.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param fileID
      * @param command
@@ -4547,8 +4603,8 @@
      * Returns the response APDU for a command APDU sent through SIM_IO.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param fileID
@@ -4576,8 +4632,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param content String containing SAT/USAT response in hexadecimal
      *                format starting with command tag. See TS 102 223 for
@@ -4594,8 +4650,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param content String containing SAT/USAT response in hexadecimal
@@ -4620,10 +4676,10 @@
     /**
      * Read one of the NV items defined in com.android.internal.telephony.RadioNVItems.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param itemID the ID of the item to read.
      * @return the NV item as a String, or null on any failure.
@@ -4646,10 +4702,10 @@
     /**
      * Write one of the NV items defined in com.android.internal.telephony.RadioNVItems.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param itemID the ID of the item to read.
      * @param itemValue the value to write, as a String.
@@ -4673,10 +4729,10 @@
     /**
      * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param preferredRoamingList byte array containing the new PRL.
      * @return true on success; false on any failure.
@@ -4700,10 +4756,10 @@
      * Perform the specified type of NV config reset. The radio will be taken offline
      * and the device must be rebooted after the operation. Used for device
      * configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
@@ -5051,8 +5107,8 @@
      * Returns the response of authentication for the default subscription.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges or READ_PRIVILEGED_PHONE_STATE
-     * permission.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
      * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
@@ -5060,9 +5116,10 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      */
+    // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
+    // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
+    // it's not public API.
     public String getIccAuthentication(int appType, int authType, String data) {
         return getIccAuthentication(getSubId(), appType, authType, data);
     }
@@ -5071,7 +5128,7 @@
      * Returns the response of USIM Authentication for specified subId.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges.
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId subscription ID used for authentication
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
@@ -5080,8 +5137,6 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      * @hide
      */
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
@@ -5102,8 +5157,12 @@
      * Returns an array of Forbidden PLMNs from the USIM App
      * Returns null if the query fails.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return an array of forbidden PLMNs or null if not available
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String[] getForbiddenPlmns() {
       return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
@@ -5292,6 +5351,23 @@
     }
 
     /**
+     * @return true if the IMS resolver is busy resolving a binding and should not be considered
+     * available, false if the IMS resolver is idle.
+     * @hide
+     */
+    public boolean isResolvingImsBinding() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isResolvingImsBinding();
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
+        }
+        return false;
+    }
+
+    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -5309,10 +5385,10 @@
     /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the preferred network type, defined in RILConstants.java.
      * @hide
@@ -5332,11 +5408,12 @@
 
     /**
      * Sets the network selection mode to automatic.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setNetworkSelectionModeAutomatic() {
         try {
@@ -5352,15 +5429,14 @@
     }
 
     /**
-     * Perform a radio scan and return the list of avialble networks.
+     * Perform a radio scan and return the list of available networks.
      *
      * The return value is a list of the OperatorInfo of the networks found. Note that this
      * scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @hide
      * TODO: Add an overload that takes no args.
@@ -5384,17 +5460,16 @@
      * This method is asynchronous, so the network scan results will be returned by callback.
      * The returned NetworkScan will contain a callback method which can be used to stop the scan.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges()
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
      * @param executor The executor through which the callback should be invoked.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
             NetworkScanRequest request, Executor executor,
@@ -5422,10 +5497,9 @@
     /**
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param operatorNumeric the PLMN ID of the network to select.
      * @param persistSelection whether the selection will persist until reboot. If true, only allows
@@ -5433,6 +5507,7 @@
      * normal network selection next time.
      * @return true on success; false on any failure.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
         try {
@@ -5452,10 +5527,10 @@
     /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type, defined in RILConstants.java.
@@ -5479,9 +5554,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      */
@@ -5492,9 +5565,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      * @hide
@@ -5584,8 +5655,7 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
@@ -5602,8 +5672,7 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param brand The brand name to display/set.
@@ -6257,15 +6326,15 @@
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
      * <p>Requires Permission:
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
-     *     calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param enable Whether to enable mobile data.
      *
-     * @see #hasCarrierPrivileges
      * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(boolean enable) {
         setUserMobileDataEnabled(enable);
@@ -6302,7 +6371,7 @@
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * <p>Note that this does not take into account any data restrictions that may be present on the
      * calling app. Such restrictions may be inspected with
@@ -6310,7 +6379,6 @@
      *
      * @return true if mobile data is enabled.
      *
-     * @see #hasCarrierPrivileges
      * @deprecated use {@link #isUserMobileDataEnabled()} instead.
      */
     @Deprecated
@@ -7081,7 +7149,11 @@
 
     /**
      * Returns the current {@link ServiceState} information.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
@@ -7127,14 +7199,14 @@
     /**
      * Sets the per-account voicemail ringtone.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
      * voicemail ringtone.
      * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -7172,14 +7244,14 @@
     /**
      * Sets the per-account preference whether vibration is enabled for voicemail notifications.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
      * voicemail vibration setting.
      * @param enabled Whether to enable or disable vibration for voicemail notifications from a
      * specific PhoneAccount.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -7600,12 +7672,10 @@
      * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
      * <p>Requires Permission:
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
-     *     calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}.
      *
      * @param enable Whether to enable mobile data.
-     *
-     * @see #hasCarrierPrivileges
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setUserMobileDataEnabled(boolean enable) {
@@ -7623,15 +7693,13 @@
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}.
      *
      * <p>Note that this does not take into account any data restrictions that may be present on the
      * calling app. Such restrictions may be inspected with
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
-     *
-     * @see #hasCarrierPrivileges
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.ACCESS_NETWORK_STATE,
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 946cecf..99e2db8 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -145,7 +145,8 @@
                         break;
                     case CALLBACK_SCAN_ERROR:
                         try {
-                            executor.execute(() -> callback.onError(message.arg1));
+                            final int errorCode = message.arg1;
+                            executor.execute(() -> callback.onError(errorCode));
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onError", e);
                         }
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 0c17147..125161d 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -60,6 +60,7 @@
     private final String mCardId;
     private final @CardStateInfo int mCardStateInfo;
     private final int mLogicalSlotIdx;
+    private final boolean mIsExtendedApduSupported;
 
     public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
@@ -79,6 +80,7 @@
         mCardId = in.readString();
         mCardStateInfo = in.readInt();
         mLogicalSlotIdx = in.readInt();
+        mIsExtendedApduSupported = in.readByte() != 0;
     }
 
     @Override
@@ -88,6 +90,7 @@
         dest.writeString(mCardId);
         dest.writeInt(mCardStateInfo);
         dest.writeInt(mLogicalSlotIdx);
+        dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
     }
 
     @Override
@@ -96,12 +99,13 @@
     }
 
     public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) {
         this.mIsActive = isActive;
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mCardStateInfo = cardStateInfo;
         this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mIsExtendedApduSupported = isExtendedApduSupported;
     }
 
     public boolean getIsActive() {
@@ -125,6 +129,13 @@
         return mLogicalSlotIdx;
     }
 
+    /**
+     * @return {@code true} if this slot supports extended APDU from ATR, {@code false} otherwise.
+     */
+    public boolean getIsExtendedApduSupported() {
+        return mIsExtendedApduSupported;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -139,7 +150,8 @@
                 && (mIsEuicc == that.mIsEuicc)
                 && (mCardId == that.mCardId)
                 && (mCardStateInfo == that.mCardStateInfo)
-                && (mLogicalSlotIdx == that.mLogicalSlotIdx);
+                && (mLogicalSlotIdx == that.mLogicalSlotIdx)
+                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported);
     }
 
     @Override
@@ -150,6 +162,7 @@
         result = 31 * result + Objects.hashCode(mCardId);
         result = 31 * result + mCardStateInfo;
         result = 31 * result + mLogicalSlotIdx;
+        result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
         return result;
     }
 
@@ -165,6 +178,8 @@
                 + mCardStateInfo
                 + ", phoneId="
                 + mLogicalSlotIdx
+                + ", mIsExtendedApduSupported="
+                + mIsExtendedApduSupported
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 2748cb5..c008711 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -161,11 +161,6 @@
         }
 
         @Override
-        public void notifyImsFeatureReady(int slotId, int featureType) {
-            ImsService.this.notifyImsFeatureReady(slotId, featureType);
-        }
-
-        @Override
         public IImsConfig getConfig(int slotId) {
             ImsConfigImplBase c = ImsService.this.getConfig(slotId);
             return c != null ? c.getIImsConfig() : null;
@@ -274,25 +269,6 @@
         }
     }
 
-    private void notifyImsFeatureReady(int slotId, int featureType) {
-        synchronized (mFeaturesBySlot) {
-            // get ImsFeature associated with the slot/feature
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
-                        "slot " + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.onFeatureReady();
-        }
-    }
-
     /**
      * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
      * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index 86f8606..c7da681 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -36,8 +36,6 @@
     ImsFeatureConfiguration querySupportedImsFeatures();
     // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
     void notifyImsServiceReadyForFeatureCreation();
-    // Synchronous call to ensure the new ImsFeature is ready before using the Feature.
-    void notifyImsFeatureReady(int slotId, int featureType);
     void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
     IImsConfig getConfig(int slotId);
     IImsRegistration getRegistration(int slotId);
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 2fffd36..c073d1a 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -575,7 +575,7 @@
      */
     public ImsUtImplBase getUt() {
         // Base Implementation - Should be overridden
-        return null;
+        return new ImsUtImplBase();
     }
 
     /**
@@ -584,7 +584,7 @@
      */
     public ImsEcbmImplBase getEcbm() {
         // Base Implementation - Should be overridden
-        return null;
+        return new ImsEcbmImplBase();
     }
 
     /**
@@ -593,7 +593,7 @@
      */
     public ImsMultiEndpointImplBase getMultiEndpoint() {
         // Base Implementation - Should be overridden
-        return null;
+        return new ImsMultiEndpointImplBase();
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index 98b67c3..2f52c0a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 import android.telephony.ims.feature.ImsFeature;
 import android.util.ArraySet;
+import android.util.Pair;
 
 import java.util.Set;
 
@@ -34,14 +35,57 @@
  */
 @SystemApi
 public final class ImsFeatureConfiguration implements Parcelable {
+
+    public static final class FeatureSlotPair {
+        /**
+         * SIM slot that this feature is associated with.
+         */
+        public final int slotId;
+        /**
+         * The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public final @ImsFeature.FeatureType int featureType;
+
+        /**
+         * A mapping from slotId to IMS Feature type.
+         * @param slotId the SIM slot ID associated with this feature.
+         * @param featureType The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public FeatureSlotPair(int slotId, @ImsFeature.FeatureType int featureType) {
+            this.slotId = slotId;
+            this.featureType = featureType;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            FeatureSlotPair that = (FeatureSlotPair) o;
+
+            if (slotId != that.slotId) return false;
+            return featureType == that.featureType;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = slotId;
+            result = 31 * result + featureType;
+            return result;
+        }
+    }
+
     /**
      * Features that this ImsService supports.
      */
-    private final Set<Integer> mFeatures;
+    private final Set<FeatureSlotPair> mFeatures;
 
     /**
-     * Builder for {@link ImsFeatureConfiguration} that makes adding supported {@link ImsFeature}s
-     * easier.
+     * Builder for {@link ImsFeatureConfiguration}.
      */
     public static class Builder {
             ImsFeatureConfiguration mConfig;
@@ -50,12 +94,15 @@
         }
 
         /**
-         * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service
-         *         supports.
+         * Adds an IMS feature associated with a SIM slot ID.
+         * @param slotId The slot ID associated with the IMS feature.
+         * @param featureType The feature that the slot ID supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
          * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
          */
-        public Builder addFeature(@ImsFeature.FeatureType int feature) {
-            mConfig.addFeature(feature);
+        public Builder addFeature(int slotId, @ImsFeature.FeatureType int featureType) {
+            mConfig.addFeature(slotId, featureType);
             return this;
         }
 
@@ -66,8 +113,7 @@
 
     /**
      * Creates with all registration features empty.
-     *
-     * Consider using the provided {@link Builder} to create this configuration instead.
+     * @hide
      */
     public ImsFeatureConfiguration() {
         mFeatures = new ArraySet<>();
@@ -76,45 +122,41 @@
     /**
      * Configuration of the ImsService, which describes which features the ImsService supports
      * (for registration).
-     * @param features an array of feature integers defined in {@link ImsFeature} that describe
-     * which features this ImsService supports. Supported values are
-     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
-     * {@link ImsFeature#FEATURE_RCS}.
+     * @param features a set of {@link FeatureSlotPair}s that describe which features this
+     *         ImsService supports.
      * @hide
      */
-    public ImsFeatureConfiguration(int[] features) {
+    public ImsFeatureConfiguration(Set<FeatureSlotPair> features) {
         mFeatures = new ArraySet<>();
 
         if (features != null) {
-            for (int i : features) {
-                mFeatures.add(i);
-            }
+            mFeatures.addAll(features);
         }
     }
 
     /**
-     * @return an int[] containing the features that this ImsService supports. Supported values are
-     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
-     * {@link ImsFeature#FEATURE_RCS}.
+     * @return a set of supported slot ID to feature type pairs contained within a
+     * {@link FeatureSlotPair}.
      */
-    public int[] getServiceFeatures() {
-        return mFeatures.stream().mapToInt(i->i).toArray();
+    public Set<FeatureSlotPair> getServiceFeatures() {
+        return new ArraySet<>(mFeatures);
     }
 
-    void addFeature(int feature) {
-        mFeatures.add(feature);
+    /**
+     * @hide
+     */
+    void addFeature(int slotId, int feature) {
+        mFeatures.add(new FeatureSlotPair(slotId, feature));
     }
 
     /** @hide */
     protected ImsFeatureConfiguration(Parcel in) {
-        int[] features = in.createIntArray();
-        if (features != null) {
-            mFeatures = new ArraySet<>(features.length);
-            for(Integer i : features) {
-                mFeatures.add(i);
-            }
-        } else {
-            mFeatures = new ArraySet<>();
+        int featurePairLength = in.readInt();
+        // length
+        mFeatures = new ArraySet<>(featurePairLength);
+        for (int i = 0; i < featurePairLength; i++) {
+            // pair of reads for each entry (slotId->featureType)
+            mFeatures.add(new FeatureSlotPair(in.readInt(), in.readInt()));
         }
     }
 
@@ -138,7 +180,15 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
+        FeatureSlotPair[] featureSlotPairs = new FeatureSlotPair[mFeatures.size()];
+        mFeatures.toArray(featureSlotPairs);
+        // length of list
+        dest.writeInt(featureSlotPairs.length);
+        // then pairs of integers for each entry (slotId->featureType).
+        for (FeatureSlotPair featureSlotPair : featureSlotPairs) {
+            dest.writeInt(featureSlotPair.slotId);
+            dest.writeInt(featureSlotPair.featureType);
+        }
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4002d3c..afbb947 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -829,6 +829,12 @@
     boolean isEmergencyMmTelAvailable(int slotId);
 
     /**
+     * @return true if the IMS resolver is busy resolving a binding and should not be considered
+     * available, false if the IMS resolver is idle.
+     */
+    boolean isResolvingImsBinding();
+
+    /**
      * Set the network selection mode to automatic.
      *
      * @param subId the id of the subscription to update.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index da8471f..a182f2b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -26,7 +26,8 @@
 import android.telephony.TelephonyManager;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.ITelephony;
+
+import java.util.function.Supplier;
 
 /** Utility class for Telephony permission enforcement. */
 public final class TelephonyPermissions {
@@ -34,6 +35,9 @@
 
     private static final boolean DBG = false;
 
+    private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () ->
+            ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+
     private TelephonyPermissions() {}
 
     /**
@@ -41,8 +45,8 @@
      *
      * <p>This method behaves in one of the following ways:
      * <ul>
-     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
-     *       READ_PHONE_STATE runtime permission.
+     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
+     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
      *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
      *       apps which support runtime permissions, if the caller does not currently have any of
      *       these permissions.
@@ -51,20 +55,30 @@
      *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
      *       so we return false to indicate that the calling function should return dummy data.
      * </ul>
+     *
+     * <p>Note: for simplicity, this method always returns false for callers using legacy
+     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
+     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
+     * devices.
+     *
+     * @param subId the subId of the relevant subscription; used to check carrier privileges. May be
+     *              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} to skip this check for cases
+     *              where it isn't relevant (hidden APIs, or APIs which are otherwise okay to leave
+     *              inaccesible to carrier-privileged apps).
      */
     public static boolean checkCallingOrSelfReadPhoneState(
-            Context context, String callingPackage, String message) {
-        return checkReadPhoneState(context, Binder.getCallingPid(), Binder.getCallingUid(),
+            Context context, int subId, String callingPackage, String message) {
+        return checkReadPhoneState(context, subId, Binder.getCallingPid(), Binder.getCallingUid(),
                 callingPackage, message);
     }
 
     /**
      * Check whether the app with the given pid/uid can read phone state.
      *
-     * <p>This method behaves in one of the following ways:
+    * <p>This method behaves in one of the following ways:
      * <ul>
-     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
-     *       READ_PHONE_STATE runtime permission.
+     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
+     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
      *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
      *       apps which support runtime permissions, if the caller does not currently have any of
      *       these permissions.
@@ -73,9 +87,22 @@
      *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
      *       so we return false to indicate that the calling function should return dummy data.
      * </ul>
+     *
+     * <p>Note: for simplicity, this method always returns false for callers using legacy
+     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
+     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
+     * devices.
      */
     public static boolean checkReadPhoneState(
-            Context context, int pid, int uid, String callingPackage, String message) {
+            Context context, int subId, int pid, int uid, String callingPackage, String message) {
+        return checkReadPhoneState(
+                context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
+    }
+
+    @VisibleForTesting
+    public static boolean checkReadPhoneState(
+            Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
+            String callingPackage, String message) {
         try {
             context.enforcePermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
@@ -83,8 +110,18 @@
             // SKIP checking for run-time permission since caller has PRIVILEGED permission
             return true;
         } catch (SecurityException privilegedPhoneStateException) {
-            context.enforcePermission(
-                    android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+            try {
+                context.enforcePermission(
+                        android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+            } catch (SecurityException phoneStateException) {
+                // If we don't have the runtime permission, but do have carrier privileges, that
+                // suffices for reading phone state.
+                if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                    enforceCarrierPrivilege(telephonySupplier, subId, uid, message);
+                    return true;
+                }
+                throw phoneStateException;
+            }
         }
 
         // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
@@ -101,14 +138,16 @@
      * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
      */
     public static boolean checkCallingOrSelfReadPhoneNumber(
-            Context context, String callingPackage, String message) {
+            Context context, int subId, String callingPackage, String message) {
         return checkReadPhoneNumber(
-                context, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
+                context, TELEPHONY_SUPPLIER, subId, Binder.getCallingPid(), Binder.getCallingUid(),
+                callingPackage, message);
     }
 
     @VisibleForTesting
     public static boolean checkReadPhoneNumber(
-            Context context, int pid, int uid, String callingPackage, String message) {
+            Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
+            String callingPackage, String message) {
         // Default SMS app can always read it.
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, uid, callingPackage) ==
@@ -121,7 +160,8 @@
 
         // First, check if we can read the phone state.
         try {
-            return checkReadPhoneState(context, pid, uid, callingPackage, message);
+            return checkReadPhoneState(
+                    context, telephonySupplier, subId, pid, uid, callingPackage, message);
         } catch (SecurityException readPhoneStateSecurityException) {
         }
         // Can be read with READ_SMS too.
@@ -186,16 +226,21 @@
     }
 
     private static void enforceCarrierPrivilege(int subId, int uid, String message) {
-        if (getCarrierPrivilegeStatus(subId, uid) !=
+        enforceCarrierPrivilege(TELEPHONY_SUPPLIER, subId, uid, message);
+    }
+
+    private static void enforceCarrierPrivilege(
+            Supplier<ITelephony> telephonySupplier, int subId, int uid, String message) {
+        if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid) !=
                 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
             if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
             throw new SecurityException(message);
         }
     }
 
-    private static int getCarrierPrivilegeStatus(int subId, int uid) {
-        ITelephony telephony =
-                ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+    private static int getCarrierPrivilegeStatus(
+            Supplier<ITelephony> telephonySupplier, int subId, int uid) {
+        ITelephony telephony = telephonySupplier.get();
         try {
             if (telephony != null) {
                 return telephony.getCarrierPrivilegeStatusForUid(subId, uid);
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index 936a1f2..4705e1d 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -189,7 +189,8 @@
     }
 
     /**
-     * @return The Location Configuration Information (LCI) as self-reported by the peer.
+     * @return The Location Configuration Information (LCI) as self-reported by the peer. The format
+     * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.10.
      * <p>
      * Note: the information is NOT validated - use with caution. Consider validating it with
      * other sources of information before using it.
@@ -207,7 +208,8 @@
     }
 
     /**
-     * @return The Location Civic report (LCR) as self-reported by the peer.
+     * @return The Location Civic report (LCR) as self-reported by the peer. The format
+     * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.13.
      * <p>
      * Note: the information is NOT validated - use with caution. Consider validating it with
      * other sources of information before using it.